hasMethod('__set_state')) { $method = $reflectionObject->getMethod('__set_state'); return $method->isPublic() && $method->isStatic(); } return false; } /** * {@inheritDoc} */ public function export(object $object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array { $className = $reflectionObject->getName(); $vars = $this->getObjectVars($object, $path); $exportedVars = $this->exporter->exportArray($vars, $path, $parentIds); $exportedVars = $this->exporter->wrap($exportedVars, '\\' . $className . '::__set_state(', ')'); return $exportedVars; } /** * Returns public and private object properties, as an associative array. * * This is unlike get_object_vars(), which only returns properties accessible from the current scope. * * The returned values are in line with those returned by var_export() in the array passed to __set_state(); unlike * var_export() however, this method throws an exception if the object has overridden private properties, as this * would result in a conflict in array keys. In this case, var_export() would return multiple values in the output, * which once executed would yield an array containing only the last value for this key in the output. * * This way we offer a better safety guarantee, while staying compatible with var_export() in the output. * * @psalm-suppress MixedAssignment * * @param object $object The object to dump. * @param string[] $path The path to the object, in the array/object graph. * * @return array An associative array of property name to value. * * @throws ExportException */ private function getObjectVars(object $object, array $path) : array { $result = []; foreach ((array) $object as $name => $value) { $name = (string) $name; $pos = strrpos($name, "\0"); if ($pos !== false) { $name = substr($name, $pos + 1); } assert($name !== false); if (array_key_exists($name, $result)) { $className = get_class($object); throw new ExportException( 'Class "' . $className . '" has overridden private property "' . $name . '". ' . 'This is not supported for exporting objects with __set_state().', $path ); } if ($this->exporter->skipDynamicProperties && $this->isDynamicProperty($object, $name)) { continue; } $result[$name] = $value; } return $result; } /** * @param object $object * @param string $name * * @return bool */ private function isDynamicProperty(object $object, string $name) : bool { $reflectionClass = new \ReflectionClass($object); $reflectionObject = new \ReflectionObject($object); return $reflectionObject->hasProperty($name) && ! $reflectionClass->hasProperty($name); } }