I have re write my code from old php 7.4 scripts. And i see my code be more slower than old version
My old models export data from method toArray
And his representation was like that
public function toArray()
{
return [
"id" => $this->id;
"name" => $this->name;
//And many other values
]
}
But now, with PHP 8, i have use reflection object to serialize my object. That take something like :
class MyModel extends Model
{
#[AttributeExport]
private int $id;
#[AttributeExport]
private string $name;
}
//In Model
abstract Model implement JsonSerialize
{
public function jsonSerialize(): array
{
$data = [];
$reflection = new \ReflectionClass(get_called_class());
$elements = array_merge($reflection->getProperties(), $reflection->getMethods());
foreach ($elements as $element) {
$exportableAttributes = $element->getAttributes(
AttributeExport::class,
\ReflectionAttribute::IS_INSTANCEOF
);
foreach ($exportableAttributes as $exportableAttribute) {
/** @var AttributeExport $exportableInstance */
$exportableInstance = $exportableAttribute->newInstance();
$exportName = $exportableInstance->hasName() ? $exportableInstance->getName() : $element->getName();
$method = $element->getName();
if ($element instanceof \ReflectionProperty) {
$method = "get" . ucfirst($method);
}
$data[$exportName] = $this->{$method}();
}
}
return $data;
}
}
I may be mistakenly thinking that the problem may come from there. But do you think that on a large volume of data, this strategy can have an impact?
I used more abstract class compared to the old version to avoid code duplication. Dont know if that can impact too
There are two things to consider, you changed from direct property access to a reflection based getter method approach:
Have a look at this modification, i added a simple cache, now the runtime of the reflection approach is much faster, i think the performace hit should be acceptable in the most settings:
string(29) "reflection: 0.036892890930176"
string(33) "direct acccess: 0.015763998031616"
string(33) "direct methods: 0.018218040466309"
<?php
#[Attribute]
class AttributeExport
{
public function __construct()
{
}
public function hasName()
{
return false;
}
}
abstract class Model implements JsonSerializable
{
private static $cache = [];
public function jsonSerialize():array
{
$data = [];
if (!isset(self::$cache[self::class]))
{
self::$cache[self::class ] = [];
$reflection = new \ReflectionClass(get_called_class());
$elements = array_merge($reflection->getProperties() , $reflection->getMethods());
foreach ($elements as $element)
{
$exportableAttributes = $element->getAttributes(AttributeExport::class , \ReflectionAttribute::IS_INSTANCEOF);
foreach ($exportableAttributes as $exportableAttribute)
{
/** @var AttributeExport $exportableInstance */
$exportableInstance = $exportableAttribute->newInstance();
$exportName = $exportableInstance->hasName() ? $exportableInstance->getName() : $element->getName();
$method = $element->getName();
if ($element instanceof \ReflectionProperty)
{
$method = "get" . ucfirst($method);
}
self::$cache[self::class][$exportName] = $method;
}
}
}
foreach (self::$cache[self::class] as $exportName => $method)
{
$data[$exportName] = $this->{$method}();
}
return $data;
}
}
class MyModel extends Model
{
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
#[AttributeExport]
private int $id;
#[AttributeExport]
private string $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function toArray()
{
return [
"id" => $this->id,
"name" => $this->name
//And many other values
];
}
public function toArrayMethods()
{
return [
"id" => $this->getId(),
"name" => $this->getName()
//And many other values
];
}
}
$z = new MyModel(1, "a");
$zr = $z->jsonSerialize();
$zr2 = $z->toArray();
$time1 = microtime(true);
for ($a = 0;$a < 100000;$a++)
{
$x = new MyModel(1, "a");
$r = $x->jsonSerialize();
}
$timea = microtime(true) - $time1;
$time2 = microtime(true);
for ($b = 1;$b < 100000;$b++)
{
$x2 = new MyModel(1, "a");
$r2 = $x2->toArray();
}
$timeb = microtime(true) - $time2;
$time3 = microtime(true);
for ($b = 1;$b < 100000;$b++)
{
$x2 = new MyModel(1, "a");
$r2 = $x2->toArrayMethods();
}
$timec = microtime(true) - $time3;
var_dump("reflection: " . $timea);
var_dump("direct acccess: " . $timeb);
var_dump("direct methods: " . $timec);
Using reflection to access the properties without getter could also be measured.