Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Document;
   4  
   5  use DateTime;
   6  use PhpOffice\PhpSpreadsheet\Shared\IntOrFloat;
   7  
   8  class Properties
   9  {
  10      /** constants */
  11      public const PROPERTY_TYPE_BOOLEAN = 'b';
  12      public const PROPERTY_TYPE_INTEGER = 'i';
  13      public const PROPERTY_TYPE_FLOAT = 'f';
  14      public const PROPERTY_TYPE_DATE = 'd';
  15      public const PROPERTY_TYPE_STRING = 's';
  16      public const PROPERTY_TYPE_UNKNOWN = 'u';
  17  
  18      private const VALID_PROPERTY_TYPE_LIST = [
  19          self::PROPERTY_TYPE_BOOLEAN,
  20          self::PROPERTY_TYPE_INTEGER,
  21          self::PROPERTY_TYPE_FLOAT,
  22          self::PROPERTY_TYPE_DATE,
  23          self::PROPERTY_TYPE_STRING,
  24      ];
  25  
  26      /**
  27       * Creator.
  28       *
  29       * @var string
  30       */
  31      private $creator = 'Unknown Creator';
  32  
  33      /**
  34       * LastModifiedBy.
  35       *
  36       * @var string
  37       */
  38      private $lastModifiedBy;
  39  
  40      /**
  41       * Created.
  42       *
  43       * @var float|int
  44       */
  45      private $created;
  46  
  47      /**
  48       * Modified.
  49       *
  50       * @var float|int
  51       */
  52      private $modified;
  53  
  54      /**
  55       * Title.
  56       *
  57       * @var string
  58       */
  59      private $title = 'Untitled Spreadsheet';
  60  
  61      /**
  62       * Description.
  63       *
  64       * @var string
  65       */
  66      private $description = '';
  67  
  68      /**
  69       * Subject.
  70       *
  71       * @var string
  72       */
  73      private $subject = '';
  74  
  75      /**
  76       * Keywords.
  77       *
  78       * @var string
  79       */
  80      private $keywords = '';
  81  
  82      /**
  83       * Category.
  84       *
  85       * @var string
  86       */
  87      private $category = '';
  88  
  89      /**
  90       * Manager.
  91       *
  92       * @var string
  93       */
  94      private $manager = '';
  95  
  96      /**
  97       * Company.
  98       *
  99       * @var string
 100       */
 101      private $company = '';
 102  
 103      /**
 104       * Custom Properties.
 105       *
 106       * @var array{value: mixed, type: string}[]
 107       */
 108      private $customProperties = [];
 109  
 110      private string $hyperlinkBase = '';
 111  
 112      /**
 113       * Create a new Document Properties instance.
 114       */
 115      public function __construct()
 116      {
 117          // Initialise values
 118          $this->lastModifiedBy = $this->creator;
 119          $this->created = self::intOrFloatTimestamp(null);
 120          $this->modified = $this->created;
 121      }
 122  
 123      /**
 124       * Get Creator.
 125       */
 126      public function getCreator(): string
 127      {
 128          return $this->creator;
 129      }
 130  
 131      /**
 132       * Set Creator.
 133       *
 134       * @return $this
 135       */
 136      public function setCreator(string $creator): self
 137      {
 138          $this->creator = $creator;
 139  
 140          return $this;
 141      }
 142  
 143      /**
 144       * Get Last Modified By.
 145       */
 146      public function getLastModifiedBy(): string
 147      {
 148          return $this->lastModifiedBy;
 149      }
 150  
 151      /**
 152       * Set Last Modified By.
 153       *
 154       * @return $this
 155       */
 156      public function setLastModifiedBy(string $modifiedBy): self
 157      {
 158          $this->lastModifiedBy = $modifiedBy;
 159  
 160          return $this;
 161      }
 162  
 163      /**
 164       * @param null|float|int|string $timestamp
 165       *
 166       * @return float|int
 167       */
 168      private static function intOrFloatTimestamp($timestamp)
 169      {
 170          if ($timestamp === null) {
 171              $timestamp = (float) (new DateTime())->format('U');
 172          } elseif (is_string($timestamp)) {
 173              if (is_numeric($timestamp)) {
 174                  $timestamp = (float) $timestamp;
 175              } else {
 176                  $timestamp = (string) preg_replace('/[.][0-9]*$/', '', $timestamp);
 177                  $timestamp = (string) preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp);
 178                  $timestamp = (string) preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp);
 179                  $timestamp = (float) (new DateTime($timestamp))->format('U');
 180              }
 181          }
 182  
 183          return IntOrFloat::evaluate($timestamp);
 184      }
 185  
 186      /**
 187       * Get Created.
 188       *
 189       * @return float|int
 190       */
 191      public function getCreated()
 192      {
 193          return $this->created;
 194      }
 195  
 196      /**
 197       * Set Created.
 198       *
 199       * @param null|float|int|string $timestamp
 200       *
 201       * @return $this
 202       */
 203      public function setCreated($timestamp): self
 204      {
 205          $this->created = self::intOrFloatTimestamp($timestamp);
 206  
 207          return $this;
 208      }
 209  
 210      /**
 211       * Get Modified.
 212       *
 213       * @return float|int
 214       */
 215      public function getModified()
 216      {
 217          return $this->modified;
 218      }
 219  
 220      /**
 221       * Set Modified.
 222       *
 223       * @param null|float|int|string $timestamp
 224       *
 225       * @return $this
 226       */
 227      public function setModified($timestamp): self
 228      {
 229          $this->modified = self::intOrFloatTimestamp($timestamp);
 230  
 231          return $this;
 232      }
 233  
 234      /**
 235       * Get Title.
 236       */
 237      public function getTitle(): string
 238      {
 239          return $this->title;
 240      }
 241  
 242      /**
 243       * Set Title.
 244       *
 245       * @return $this
 246       */
 247      public function setTitle(string $title): self
 248      {
 249          $this->title = $title;
 250  
 251          return $this;
 252      }
 253  
 254      /**
 255       * Get Description.
 256       */
 257      public function getDescription(): string
 258      {
 259          return $this->description;
 260      }
 261  
 262      /**
 263       * Set Description.
 264       *
 265       * @return $this
 266       */
 267      public function setDescription(string $description): self
 268      {
 269          $this->description = $description;
 270  
 271          return $this;
 272      }
 273  
 274      /**
 275       * Get Subject.
 276       */
 277      public function getSubject(): string
 278      {
 279          return $this->subject;
 280      }
 281  
 282      /**
 283       * Set Subject.
 284       *
 285       * @return $this
 286       */
 287      public function setSubject(string $subject): self
 288      {
 289          $this->subject = $subject;
 290  
 291          return $this;
 292      }
 293  
 294      /**
 295       * Get Keywords.
 296       */
 297      public function getKeywords(): string
 298      {
 299          return $this->keywords;
 300      }
 301  
 302      /**
 303       * Set Keywords.
 304       *
 305       * @return $this
 306       */
 307      public function setKeywords(string $keywords): self
 308      {
 309          $this->keywords = $keywords;
 310  
 311          return $this;
 312      }
 313  
 314      /**
 315       * Get Category.
 316       */
 317      public function getCategory(): string
 318      {
 319          return $this->category;
 320      }
 321  
 322      /**
 323       * Set Category.
 324       *
 325       * @return $this
 326       */
 327      public function setCategory(string $category): self
 328      {
 329          $this->category = $category;
 330  
 331          return $this;
 332      }
 333  
 334      /**
 335       * Get Company.
 336       */
 337      public function getCompany(): string
 338      {
 339          return $this->company;
 340      }
 341  
 342      /**
 343       * Set Company.
 344       *
 345       * @return $this
 346       */
 347      public function setCompany(string $company): self
 348      {
 349          $this->company = $company;
 350  
 351          return $this;
 352      }
 353  
 354      /**
 355       * Get Manager.
 356       */
 357      public function getManager(): string
 358      {
 359          return $this->manager;
 360      }
 361  
 362      /**
 363       * Set Manager.
 364       *
 365       * @return $this
 366       */
 367      public function setManager(string $manager): self
 368      {
 369          $this->manager = $manager;
 370  
 371          return $this;
 372      }
 373  
 374      /**
 375       * Get a List of Custom Property Names.
 376       *
 377       * @return string[]
 378       */
 379      public function getCustomProperties(): array
 380      {
 381          return array_keys($this->customProperties);
 382      }
 383  
 384      /**
 385       * Check if a Custom Property is defined.
 386       */
 387      public function isCustomPropertySet(string $propertyName): bool
 388      {
 389          return array_key_exists($propertyName, $this->customProperties);
 390      }
 391  
 392      /**
 393       * Get a Custom Property Value.
 394       *
 395       * @return mixed
 396       */
 397      public function getCustomPropertyValue(string $propertyName)
 398      {
 399          if (isset($this->customProperties[$propertyName])) {
 400              return $this->customProperties[$propertyName]['value'];
 401          }
 402  
 403          return null;
 404      }
 405  
 406      /**
 407       * Get a Custom Property Type.
 408       *
 409       * @return null|string
 410       */
 411      public function getCustomPropertyType(string $propertyName)
 412      {
 413          return $this->customProperties[$propertyName]['type'] ?? null;
 414      }
 415  
 416      /**
 417       * @param mixed $propertyValue
 418       */
 419      private function identifyPropertyType($propertyValue): string
 420      {
 421          if (is_float($propertyValue)) {
 422              return self::PROPERTY_TYPE_FLOAT;
 423          }
 424          if (is_int($propertyValue)) {
 425              return self::PROPERTY_TYPE_INTEGER;
 426          }
 427          if (is_bool($propertyValue)) {
 428              return self::PROPERTY_TYPE_BOOLEAN;
 429          }
 430  
 431          return self::PROPERTY_TYPE_STRING;
 432      }
 433  
 434      /**
 435       * Set a Custom Property.
 436       *
 437       * @param mixed $propertyValue
 438       * @param string $propertyType
 439       *   'i' : Integer
 440       *   'f' : Floating Point
 441       *   's' : String
 442       *   'd' : Date/Time
 443       *   'b' : Boolean
 444       *
 445       * @return $this
 446       */
 447      public function setCustomProperty(string $propertyName, $propertyValue = '', $propertyType = null): self
 448      {
 449          if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
 450              $propertyType = $this->identifyPropertyType($propertyValue);
 451          }
 452  
 453          if (!is_object($propertyValue)) {
 454              $this->customProperties[$propertyName] = [
 455                  'value' => self::convertProperty($propertyValue, $propertyType),
 456                  'type' => $propertyType,
 457              ];
 458          }
 459  
 460          return $this;
 461      }
 462  
 463      private const PROPERTY_TYPE_ARRAY = [
 464          'i' => self::PROPERTY_TYPE_INTEGER,      //    Integer
 465          'i1' => self::PROPERTY_TYPE_INTEGER,     //    1-Byte Signed Integer
 466          'i2' => self::PROPERTY_TYPE_INTEGER,     //    2-Byte Signed Integer
 467          'i4' => self::PROPERTY_TYPE_INTEGER,     //    4-Byte Signed Integer
 468          'i8' => self::PROPERTY_TYPE_INTEGER,     //    8-Byte Signed Integer
 469          'int' => self::PROPERTY_TYPE_INTEGER,    //    Integer
 470          'ui1' => self::PROPERTY_TYPE_INTEGER,    //    1-Byte Unsigned Integer
 471          'ui2' => self::PROPERTY_TYPE_INTEGER,    //    2-Byte Unsigned Integer
 472          'ui4' => self::PROPERTY_TYPE_INTEGER,    //    4-Byte Unsigned Integer
 473          'ui8' => self::PROPERTY_TYPE_INTEGER,    //    8-Byte Unsigned Integer
 474          'uint' => self::PROPERTY_TYPE_INTEGER,   //    Unsigned Integer
 475          'f' => self::PROPERTY_TYPE_FLOAT,        //    Real Number
 476          'r4' => self::PROPERTY_TYPE_FLOAT,       //    4-Byte Real Number
 477          'r8' => self::PROPERTY_TYPE_FLOAT,       //    8-Byte Real Number
 478          'decimal' => self::PROPERTY_TYPE_FLOAT,  //    Decimal
 479          's' => self::PROPERTY_TYPE_STRING,       //    String
 480          'empty' => self::PROPERTY_TYPE_STRING,   //    Empty
 481          'null' => self::PROPERTY_TYPE_STRING,    //    Null
 482          'lpstr' => self::PROPERTY_TYPE_STRING,   //    LPSTR
 483          'lpwstr' => self::PROPERTY_TYPE_STRING,  //    LPWSTR
 484          'bstr' => self::PROPERTY_TYPE_STRING,    //    Basic String
 485          'd' => self::PROPERTY_TYPE_DATE,         //    Date and Time
 486          'date' => self::PROPERTY_TYPE_DATE,      //    Date and Time
 487          'filetime' => self::PROPERTY_TYPE_DATE,  //    File Time
 488          'b' => self::PROPERTY_TYPE_BOOLEAN,      //    Boolean
 489          'bool' => self::PROPERTY_TYPE_BOOLEAN,   //    Boolean
 490      ];
 491  
 492      private const SPECIAL_TYPES = [
 493          'empty' => '',
 494          'null' => null,
 495      ];
 496  
 497      /**
 498       * Convert property to form desired by Excel.
 499       *
 500       * @param mixed $propertyValue
 501       *
 502       * @return mixed
 503       */
 504      public static function convertProperty($propertyValue, string $propertyType)
 505      {
 506          return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
 507      }
 508  
 509      /**
 510       * Convert property to form desired by Excel.
 511       *
 512       * @param mixed $propertyValue
 513       *
 514       * @return mixed
 515       */
 516      private static function convertProperty2($propertyValue, string $type)
 517      {
 518          $propertyType = self::convertPropertyType($type);
 519          switch ($propertyType) {
 520              case self::PROPERTY_TYPE_INTEGER:
 521                  $intValue = (int) $propertyValue;
 522  
 523                  return ($type[0] === 'u') ? abs($intValue) : $intValue;
 524              case self::PROPERTY_TYPE_FLOAT:
 525                  return (float) $propertyValue;
 526              case self::PROPERTY_TYPE_DATE:
 527                  return self::intOrFloatTimestamp($propertyValue);
 528              case self::PROPERTY_TYPE_BOOLEAN:
 529                  return is_bool($propertyValue) ? $propertyValue : ($propertyValue === 'true');
 530              default: // includes string
 531                  return $propertyValue;
 532          }
 533      }
 534  
 535      public static function convertPropertyType(string $propertyType): string
 536      {
 537          return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;
 538      }
 539  
 540      public function getHyperlinkBase(): string
 541      {
 542          return $this->hyperlinkBase;
 543      }
 544  
 545      public function setHyperlinkBase(string $hyperlinkBase): self
 546      {
 547          $this->hyperlinkBase = $hyperlinkBase;
 548  
 549          return $this;
 550      }
 551  }