<?php
namespace PhpOffice\PhpSpreadsheet\Document;
> use DateTime;
class Properties
> use PhpOffice\PhpSpreadsheet\Shared\IntOrFloat;
{
>
/** constants */
< const PROPERTY_TYPE_BOOLEAN = 'b';
< const PROPERTY_TYPE_INTEGER = 'i';
< const PROPERTY_TYPE_FLOAT = 'f';
< const PROPERTY_TYPE_DATE = 'd';
< const PROPERTY_TYPE_STRING = 's';
< const PROPERTY_TYPE_UNKNOWN = 'u';
> public const PROPERTY_TYPE_BOOLEAN = 'b';
> public const PROPERTY_TYPE_INTEGER = 'i';
> public const PROPERTY_TYPE_FLOAT = 'f';
> public const PROPERTY_TYPE_DATE = 'd';
> public const PROPERTY_TYPE_STRING = 's';
> public const PROPERTY_TYPE_UNKNOWN = 'u';
>
> private const VALID_PROPERTY_TYPE_LIST = [
> self::PROPERTY_TYPE_BOOLEAN,
> self::PROPERTY_TYPE_INTEGER,
> self::PROPERTY_TYPE_FLOAT,
> self::PROPERTY_TYPE_DATE,
> self::PROPERTY_TYPE_STRING,
> ];
/**
* Creator.
*
* @var string
*/
private $creator = 'Unknown Creator';
/**
* LastModifiedBy.
*
* @var string
*/
private $lastModifiedBy;
/**
* Created.
*
< * @var int
> * @var float|int
*/
private $created;
/**
* Modified.
*
< * @var int
> * @var float|int
*/
private $modified;
/**
* Title.
*
* @var string
*/
private $title = 'Untitled Spreadsheet';
/**
* Description.
*
* @var string
*/
private $description = '';
/**
* Subject.
*
* @var string
*/
private $subject = '';
/**
* Keywords.
*
* @var string
*/
private $keywords = '';
/**
* Category.
*
* @var string
*/
private $category = '';
/**
* Manager.
*
* @var string
*/
private $manager = '';
/**
* Company.
*
* @var string
*/
< private $company = 'Microsoft Corporation';
> private $company = '';
/**
* Custom Properties.
*
< * @var string
> * @var array{value: mixed, type: string}[]
*/
private $customProperties = [];
/**
* Create a new Document Properties instance.
*/
public function __construct()
{
// Initialise values
$this->lastModifiedBy = $this->creator;
< $this->created = time();
< $this->modified = time();
> $this->created = self::intOrFloatTimestamp(null);
> $this->modified = $this->created;
}
/**
* Get Creator.
< *
< * @return string
*/
< public function getCreator()
> public function getCreator(): string
{
return $this->creator;
}
/**
* Set Creator.
*
< * @param string $creator
< *
* @return $this
*/
< public function setCreator($creator)
> public function setCreator(string $creator): self
{
$this->creator = $creator;
return $this;
}
/**
* Get Last Modified By.
< *
< * @return string
*/
< public function getLastModifiedBy()
> public function getLastModifiedBy(): string
{
return $this->lastModifiedBy;
}
/**
* Set Last Modified By.
*
< * @param string $pValue
< *
* @return $this
*/
< public function setLastModifiedBy($pValue)
> public function setLastModifiedBy(string $modifiedBy): self
{
< $this->lastModifiedBy = $pValue;
> $this->lastModifiedBy = $modifiedBy;
return $this;
}
/**
> * @param null|float|int|string $timestamp
* Get Created.
> *
*
> * @return float|int
* @return int
> */
*/
> private static function intOrFloatTimestamp($timestamp)
public function getCreated()
> {
{
> if ($timestamp === null) {
return $this->created;
> $timestamp = (float) (new DateTime())->format('U');
}
> } elseif (is_string($timestamp)) {
> if (is_numeric($timestamp)) {
/**
> $timestamp = (float) $timestamp;
* Set Created.
> } else {
*
> $timestamp = (string) preg_replace('/[.][0-9]*$/', '', $timestamp);
* @param int|string $time
> $timestamp = (string) preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp);
*
> $timestamp = (string) preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp);
* @return $this
> $timestamp = (float) (new DateTime($timestamp))->format('U');
*/
> }
public function setCreated($time)
> }
{
>
if ($time === null) {
> return IntOrFloat::evaluate($timestamp);
$time = time();
> }
} elseif (is_string($time)) {
>
if (is_numeric($time)) {
> /**
< * @return int
> * @return float|int
< * @param int|string $time
> * @param null|float|int|string $timestamp
< public function setCreated($time)
> public function setCreated($timestamp): self
< if ($time === null) {
< $time = time();
< } elseif (is_string($time)) {
< if (is_numeric($time)) {
< $time = (int) $time;
< } else {
< $time = strtotime($time);
< }
< }
<
< $this->created = $time;
> $this->created = self::intOrFloatTimestamp($timestamp);
< * @return int
> * @return float|int
*/
public function getModified()
{
return $this->modified;
}
/**
* Set Modified.
*
< * @param int|string $time
> * @param null|float|int|string $timestamp
*
* @return $this
*/
< public function setModified($time)
> public function setModified($timestamp): self
{
< if ($time === null) {
< $time = time();
< } elseif (is_string($time)) {
< if (is_numeric($time)) {
< $time = (int) $time;
< } else {
< $time = strtotime($time);
< }
< }
<
< $this->modified = $time;
> $this->modified = self::intOrFloatTimestamp($timestamp);
return $this;
}
/**
* Get Title.
< *
< * @return string
*/
< public function getTitle()
> public function getTitle(): string
{
return $this->title;
}
/**
* Set Title.
*
< * @param string $title
< *
* @return $this
*/
< public function setTitle($title)
> public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
/**
* Get Description.
< *
< * @return string
*/
< public function getDescription()
> public function getDescription(): string
{
return $this->description;
}
/**
* Set Description.
*
< * @param string $description
< *
* @return $this
*/
< public function setDescription($description)
> public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
/**
* Get Subject.
< *
< * @return string
*/
< public function getSubject()
> public function getSubject(): string
{
return $this->subject;
}
/**
* Set Subject.
*
< * @param string $subject
< *
* @return $this
*/
< public function setSubject($subject)
> public function setSubject(string $subject): self
{
$this->subject = $subject;
return $this;
}
/**
* Get Keywords.
< *
< * @return string
*/
< public function getKeywords()
> public function getKeywords(): string
{
return $this->keywords;
}
/**
* Set Keywords.
*
< * @param string $keywords
< *
* @return $this
*/
< public function setKeywords($keywords)
> public function setKeywords(string $keywords): self
{
$this->keywords = $keywords;
return $this;
}
/**
* Get Category.
< *
< * @return string
*/
< public function getCategory()
> public function getCategory(): string
{
return $this->category;
}
/**
* Set Category.
*
< * @param string $category
< *
* @return $this
*/
< public function setCategory($category)
> public function setCategory(string $category): self
{
$this->category = $category;
return $this;
}
/**
* Get Company.
< *
< * @return string
*/
< public function getCompany()
> public function getCompany(): string
{
return $this->company;
}
/**
* Set Company.
*
< * @param string $company
< *
* @return $this
*/
< public function setCompany($company)
> public function setCompany(string $company): self
{
$this->company = $company;
return $this;
}
/**
* Get Manager.
< *
< * @return string
*/
< public function getManager()
> public function getManager(): string
{
return $this->manager;
}
/**
* Set Manager.
*
< * @param string $manager
< *
* @return $this
*/
< public function setManager($manager)
> public function setManager(string $manager): self
{
$this->manager = $manager;
return $this;
}
/**
* Get a List of Custom Property Names.
*
< * @return array of string
> * @return string[]
*/
< public function getCustomProperties()
> public function getCustomProperties(): array
{
return array_keys($this->customProperties);
}
/**
* Check if a Custom Property is defined.
< *
< * @param string $propertyName
< *
< * @return bool
*/
< public function isCustomPropertySet($propertyName)
> public function isCustomPropertySet(string $propertyName): bool
{
< return isset($this->customProperties[$propertyName]);
> return array_key_exists($propertyName, $this->customProperties);
}
/**
* Get a Custom Property Value.
*
< * @param string $propertyName
< *
* @return mixed
*/
< public function getCustomPropertyValue($propertyName)
> public function getCustomPropertyValue(string $propertyName)
{
if (isset($this->customProperties[$propertyName])) {
return $this->customProperties[$propertyName]['value'];
}
>
}
> return null;
/**
* Get a Custom Property Type.
*
< * @param string $propertyName
< *
< * @return string
> * @return null|string
*/
< public function getCustomPropertyType($propertyName)
> public function getCustomPropertyType(string $propertyName)
{
< if (isset($this->customProperties[$propertyName])) {
< return $this->customProperties[$propertyName]['type'];
> return $this->customProperties[$propertyName]['type'] ?? null;
}
>
}
> /**
> * @param mixed $propertyValue
/**
> */
* Set a Custom Property.
> private function identifyPropertyType($propertyValue): string
*
> {
* @param string $propertyName
> if (is_float($propertyValue)) {
* @param mixed $propertyValue
> return self::PROPERTY_TYPE_FLOAT;
* @param string $propertyType
> }
* 'i' : Integer
> if (is_int($propertyValue)) {
* 'f' : Floating Point
> return self::PROPERTY_TYPE_INTEGER;
* 's' : String
> }
* 'd' : Date/Time
> if (is_bool($propertyValue)) {
* 'b' : Boolean
> return self::PROPERTY_TYPE_BOOLEAN;
*
> }
* @return $this
>
*/
> return self::PROPERTY_TYPE_STRING;
< * @param string $propertyName
< public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null)
> public function setCustomProperty(string $propertyName, $propertyValue = '', $propertyType = null): self
< if (
< ($propertyType === null) || (!in_array($propertyType, [self::PROPERTY_TYPE_INTEGER,
< self::PROPERTY_TYPE_FLOAT,
< self::PROPERTY_TYPE_STRING,
< self::PROPERTY_TYPE_DATE,
< self::PROPERTY_TYPE_BOOLEAN,
< ]))
< ) {
< if ($propertyValue === null) {
< $propertyType = self::PROPERTY_TYPE_STRING;
< } elseif (is_float($propertyValue)) {
< $propertyType = self::PROPERTY_TYPE_FLOAT;
< } elseif (is_int($propertyValue)) {
< $propertyType = self::PROPERTY_TYPE_INTEGER;
< } elseif (is_bool($propertyValue)) {
< $propertyType = self::PROPERTY_TYPE_BOOLEAN;
< } else {
< $propertyType = self::PROPERTY_TYPE_STRING;
< }
> if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
> $propertyType = $this->identifyPropertyType($propertyValue);
}
> if (!is_object($propertyValue)) {
$this->customProperties[$propertyName] = [
< 'value' => $propertyValue,
> 'value' => self::convertProperty($propertyValue, $propertyType),
'type' => $propertyType,
];
> }
return $this;
}
> private const PROPERTY_TYPE_ARRAY = [
/**
> 'i' => self::PROPERTY_TYPE_INTEGER, // Integer
* Implement PHP __clone to create a deep clone, not just a shallow copy.
> 'i1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Signed Integer
*/
> 'i2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Signed Integer
public function __clone()
> 'i4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Signed Integer
{
> 'i8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Signed Integer
$vars = get_object_vars($this);
> 'int' => self::PROPERTY_TYPE_INTEGER, // Integer
foreach ($vars as $key => $value) {
> 'ui1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Unsigned Integer
if (is_object($value)) {
> 'ui2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Unsigned Integer
$this->$key = clone $value;
> 'ui4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Unsigned Integer
} else {
> 'ui8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Unsigned Integer
$this->$key = $value;
> 'uint' => self::PROPERTY_TYPE_INTEGER, // Unsigned Integer
}
> 'f' => self::PROPERTY_TYPE_FLOAT, // Real Number
}
> 'r4' => self::PROPERTY_TYPE_FLOAT, // 4-Byte Real Number
}
> 'r8' => self::PROPERTY_TYPE_FLOAT, // 8-Byte Real Number
> 'decimal' => self::PROPERTY_TYPE_FLOAT, // Decimal
public static function convertProperty($propertyValue, $propertyType)
> 's' => self::PROPERTY_TYPE_STRING, // String
{
> 'empty' => self::PROPERTY_TYPE_STRING, // Empty
switch ($propertyType) {
> 'null' => self::PROPERTY_TYPE_STRING, // Null
case 'empty': // Empty
> 'lpstr' => self::PROPERTY_TYPE_STRING, // LPSTR
return '';
> 'lpwstr' => self::PROPERTY_TYPE_STRING, // LPWSTR
> 'bstr' => self::PROPERTY_TYPE_STRING, // Basic String
break;
> 'd' => self::PROPERTY_TYPE_DATE, // Date and Time
case 'null': // Null
> 'date' => self::PROPERTY_TYPE_DATE, // Date and Time
return null;
> 'filetime' => self::PROPERTY_TYPE_DATE, // File Time
> 'b' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
break;
> 'bool' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
case 'i1': // 1-Byte Signed Integer
> ];
case 'i2': // 2-Byte Signed Integer
>
case 'i4': // 4-Byte Signed Integer
> private const SPECIAL_TYPES = [
case 'i8': // 8-Byte Signed Integer
> 'empty' => '',
case 'int': // Integer
> 'null' => null,
return (int) $propertyValue;
> ];
>
< * Implement PHP __clone to create a deep clone, not just a shallow copy.
> * Convert property to form desired by Excel.
> *
> * @param mixed $propertyValue
> *
> * @return mixed
< public function __clone()
> public static function convertProperty($propertyValue, string $propertyType)
< $vars = get_object_vars($this);
< foreach ($vars as $key => $value) {
< if (is_object($value)) {
< $this->$key = clone $value;
< } else {
< $this->$key = $value;
< }
< }
> return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
< public static function convertProperty($propertyValue, $propertyType)
> /**
> * Convert property to form desired by Excel.
> *
> * @param mixed $propertyValue
> *
> * @return mixed
> */
> private static function convertProperty2($propertyValue, string $type)
case 'decimal': // Decimal
> $propertyType = self::convertPropertyType($type);
< case 'empty': // Empty
< return '';
<
< break;
< case 'null': // Null
< return null;
> case self::PROPERTY_TYPE_INTEGER:
> $intValue = (int) $propertyValue;
< break;
< case 'i1': // 1-Byte Signed Integer
< case 'i2': // 2-Byte Signed Integer
< case 'i4': // 4-Byte Signed Integer
< case 'i8': // 8-Byte Signed Integer
< case 'int': // Integer
< return (int) $propertyValue;
<
< break;
< case 'ui1': // 1-Byte Unsigned Integer
< case 'ui2': // 2-Byte Unsigned Integer
< case 'ui4': // 4-Byte Unsigned Integer
< case 'ui8': // 8-Byte Unsigned Integer
< case 'uint': // Unsigned Integer
< return abs((int) $propertyValue);
<
< break;
< case 'r4': // 4-Byte Real Number
< case 'r8': // 8-Byte Real Number
< case 'decimal': // Decimal
> return ($type[0] === 'u') ? abs($intValue) : $intValue;
> case self::PROPERTY_TYPE_FLOAT:
<
< break;
< case 'lpstr': // LPSTR
< case 'lpwstr': // LPWSTR
< case 'bstr': // Basic String
< return $propertyValue;
<
< break;
< case 'date': // Date and Time
< case 'filetime': // File Time
< return strtotime($propertyValue);
<
< break;
< case 'bool': // Boolean
< return $propertyValue == 'true';
<
< break;
< case 'cy': // Currency
< case 'error': // Error Status Code
< case 'vector': // Vector
< case 'array': // Array
< case 'blob': // Binary Blob
< case 'oblob': // Binary Blob Object
< case 'stream': // Binary Stream
< case 'ostream': // Binary Stream Object
< case 'storage': // Binary Storage
< case 'ostorage': // Binary Storage Object
< case 'vstream': // Binary Versioned Stream
< case 'clsid': // Class ID
< case 'cf': // Clipboard Data
> case self::PROPERTY_TYPE_DATE:
> return self::intOrFloatTimestamp($propertyValue);
> case self::PROPERTY_TYPE_BOOLEAN:
> return is_bool($propertyValue) ? $propertyValue : ($propertyValue === 'true');
> default: // includes string
<
< break;
<
< return $propertyValue;
< public static function convertPropertyType($propertyType)
> public static function convertPropertyType(string $propertyType): string
< switch ($propertyType) {
< case 'i1': // 1-Byte Signed Integer
< case 'i2': // 2-Byte Signed Integer
< case 'i4': // 4-Byte Signed Integer
< case 'i8': // 8-Byte Signed Integer
< case 'int': // Integer
< case 'ui1': // 1-Byte Unsigned Integer
< case 'ui2': // 2-Byte Unsigned Integer
< case 'ui4': // 4-Byte Unsigned Integer
< case 'ui8': // 8-Byte Unsigned Integer
< case 'uint': // Unsigned Integer
< return self::PROPERTY_TYPE_INTEGER;
<
< break;
< case 'r4': // 4-Byte Real Number
< case 'r8': // 8-Byte Real Number
< case 'decimal': // Decimal
< return self::PROPERTY_TYPE_FLOAT;
<
< break;
< case 'empty': // Empty
< case 'null': // Null
< case 'lpstr': // LPSTR
< case 'lpwstr': // LPWSTR
< case 'bstr': // Basic String
< return self::PROPERTY_TYPE_STRING;
<
< break;
< case 'date': // Date and Time
< case 'filetime': // File Time
< return self::PROPERTY_TYPE_DATE;
<
< break;
< case 'bool': // Boolean
< return self::PROPERTY_TYPE_BOOLEAN;
<
< break;
< case 'cy': // Currency
< case 'error': // Error Status Code
< case 'vector': // Vector
< case 'array': // Array
< case 'blob': // Binary Blob
< case 'oblob': // Binary Blob Object
< case 'stream': // Binary Stream
< case 'ostream': // Binary Stream Object
< case 'storage': // Binary Storage
< case 'ostorage': // Binary Storage Object
< case 'vstream': // Binary Versioned Stream
< case 'clsid': // Class ID
< case 'cf': // Clipboard Data
< return self::PROPERTY_TYPE_UNKNOWN;
<
< break;
< }
<
< return self::PROPERTY_TYPE_UNKNOWN;
> return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;