PHP8 反射比没有反射的 PHP7 性能更差吗

问题描述 投票:0回答:1

我已经从旧的 php 7.4 脚本重新编写了我的代码。我发现我的代码比旧版本更慢 我的旧模型从方法

toArray
导出数据 而他的代表就是这样的

public function toArray()
{
   return [
      "id" => $this->id;
      "name" => $this->name;
      //And many other values
   ]
}

但是现在,使用 PHP 8,我可以使用反射对象来序列化我的对象。 这需要类似:

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;
    }
}

我可能错误地认为问题可能出自那里。但你认为在大量数据上,这个策略能产生影响吗?

与旧版本相比,我使用了更多的抽象类,以避免代码重复。不知道是否也会有影响

performance reflection attributes php-7.4 php-8.2
1个回答
0
投票

有两件事需要考虑,您从直接属性访问更改为基于反射的 getter 方法:

  • 调用方法通常比直接访问慢
  • 反射操作成本高昂,但通常可以很好地缓存

看看这个修改,我添加了一个简单的缓存,现在反射方法的运行时间快得多,我认为在大多数设置下性能命中应该是可以接受的:

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);

也可以使用反射来访问没有 getter 的属性。

© www.soinside.com 2019 - 2024. All rights reserved.