diff --git a/psalm-baseline.xml b/psalm-baseline.xml index eb2ca18d..84923cd9 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -41,9 +41,11 @@ $this->storage[$key] $this->storage[$key] - + + $flag $ret $ret + $v Iterator @@ -58,6 +60,14 @@ is_callable($function) is_callable($function) + + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + @@ -158,6 +168,14 @@ $this->values $this->values + + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + @@ -205,6 +223,9 @@ $this[$name] + + ReturnTypeWillChange + @@ -223,6 +244,14 @@ (int) $priority (int) $priority + + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + @@ -292,6 +321,10 @@ null !== $this->queue + + ReturnTypeWillChange + ReturnTypeWillChange + @@ -305,7 +338,6 @@ $data[] $item $item - $item @@ -314,6 +346,12 @@ $item $item + + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + @@ -321,6 +359,12 @@ $item $item + + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + @@ -377,9 +421,7 @@ - - $length - $this->getEncoding() + $this->getEncoding() $this->getEncoding() @@ -424,6 +466,12 @@ $ar['foo']['bar'] $ar['foo']['bar'] + + $unserialized + + + isImmutable + assertSame @@ -500,6 +548,20 @@ $this->helper + + + $isImmutable + + + bool + + + $this->isImmutable + + + CustomArrayObject + + 'ErrorException' diff --git a/src/ArrayObject.php b/src/ArrayObject.php index 0e706db0..6cc195dd 100644 --- a/src/ArrayObject.php +++ b/src/ArrayObject.php @@ -12,6 +12,7 @@ use Serializable; use UnexpectedValueException; +use function array_key_exists; use function array_keys; use function asort; use function class_exists; @@ -92,7 +93,7 @@ public function __isset($key) } if (in_array($key, $this->protectedProperties)) { - throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + throw new Exception\InvalidArgumentException("$key is a protected property, use a different key"); } return isset($this->$key); @@ -113,7 +114,7 @@ public function __set($key, $value) } if (in_array($key, $this->protectedProperties)) { - throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + throw new Exception\InvalidArgumentException("$key is a protected property, use a different key"); } $this->$key = $value; @@ -133,7 +134,7 @@ public function __unset($key) } if (in_array($key, $this->protectedProperties)) { - throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + throw new Exception\InvalidArgumentException("$key is a protected property, use a different key"); } unset($this->$key); @@ -154,7 +155,7 @@ public function &__get($key) } if (in_array($key, $this->protectedProperties, true)) { - throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + throw new Exception\InvalidArgumentException("$key is a protected property, use a different key"); } return $this->$key; @@ -462,43 +463,43 @@ public function __unserialize($data) { $this->protectedProperties = array_keys(get_object_vars($this)); - foreach ($data as $k => $v) { - switch ($k) { - case 'flag': - $this->setFlags((int) $v); - break; - - case 'storage': - if (! is_array($v) && ! is_object($v)) { - throw new UnexpectedValueException(sprintf( - 'Cannot deserialize %s instance: corrupt storage data;' - . ' expected array or object, received %s', - self::class, - gettype($v) - )); - } - - $this->exchangeArray($v); - break; - - case 'iteratorClass': - if (! is_string($v)) { - throw new UnexpectedValueException(sprintf( - 'Cannot deserialize %s instance: invalid iteratorClass; expected string, received %s', - self::class, - is_object($v) ? get_class($v) : gettype($v) - )); - } - - $this->setIteratorClass($v); - break; - - case 'protectedProperties': - break; - - default: - $this->__set($k, $v); + // Unserialize protected internal properties first + if (array_key_exists('flag', $data)) { + $this->setFlags((int) $data['flag']); + unset($data['flag']); + } + + if (array_key_exists('storage', $data)) { + if (! is_array($data['storage']) && ! is_object($data['storage'])) { + throw new UnexpectedValueException(sprintf( + 'Cannot deserialize %s instance: corrupt storage data; expected array or object, received %s', + self::class, + gettype($data['storage']) + )); + } + $this->exchangeArray($data['storage']); + unset($data['storage']); + } + + if (array_key_exists('iteratorClass', $data)) { + if (! is_string($data['iteratorClass'])) { + throw new UnexpectedValueException(sprintf( + 'Cannot deserialize %s instance: invalid iteratorClass; expected string, received %s', + self::class, + is_object($data['iteratorClass']) + ? get_class($data['iteratorClass']) + : gettype($data['iteratorClass']) + )); } + $this->setIteratorClass($data['iteratorClass']); + unset($data['iteratorClass']); + } + + unset($data['protectedProperties']); + + // Unserialize array keys after resolving protected properties to ensure configuration is used. + foreach ($data as $k => $v) { + $this->__set($k, $v); } } } diff --git a/test/ArrayObjectTest.php b/test/ArrayObjectTest.php index 0f626847..9cf8cf73 100644 --- a/test/ArrayObjectTest.php +++ b/test/ArrayObjectTest.php @@ -401,4 +401,15 @@ public function testSerializationRestoresProperties(): void self::assertEquals($ar, unserialize(serialize($ar))); } + + public function testSerializationRestoresProtectedProperties(): void + { + $ar = new CustomArrayObject([], ArrayObject::ARRAY_AS_PROPS); + self::assertTrue($ar->isImmutable()); + + $serialized = serialize($ar); + $unserialized = unserialize($serialized); + + self::assertTrue($unserialized->isImmutable()); + } } diff --git a/test/CustomArrayObject.php b/test/CustomArrayObject.php new file mode 100644 index 00000000..40586be4 --- /dev/null +++ b/test/CustomArrayObject.php @@ -0,0 +1,18 @@ +isImmutable; + } +}