缓存使用对象的Laravel配置
#php #laravel #caching

真正的快速:如果您使用spatie/laravel-markdown并遇到此问题(我如何遇到此问题),它是最近用PR#53固定的。 ð


这个标题可能是您的头抓手。这是我最近遇到的一个问题,我没有立即挖掘。

基本上,运行artisan config:cache开始给我错误地说无法序列化我的配置。

倒带

如果您以前没有使用过Laravel,则该项目的目录之一是config/。该目录包含用于项目设置的PHP文件,主要是Laravel的,但您也可以创建和注册。一些安装的软件包还可以让您发布到该目录的 配置以供您自定义。

通常为每个请求重建组合的应用程序配置。为了加快这一速度,Laravel具有一个config:cache命令来生成1个配置文件。这包括任何可能通过函数调用等人进行动态解析的值。而且通常只是有效。

当工作不起作用时

有时您可能需要在配置中一个对象,例如您使用Spatie的Laravel Markdown软件包并创建自定义渲染器。

例如:

<‌?php
return [
  // ...
  'block_renderers' => [
    ['class' => BlockQuote::class, 'renderer' => new BlockQuoteRenderer(), 'priority' => 1],
    ['class' => Heading::class, 'renderer' => new HeadingRenderer(), 'priority' => 1],
    ['class' => ListItem::class, 'renderer' => new ListItemRenderer(), 'priority' => 1],
  ],
  //...
];

现在,这看起来并不糟糕。但是,如果您这样做然后运行config:cache,您将获得与此类似的错误:

  LogicException

  Your configuration files are not serializable.

  at /path/to/project/vendor/laravel/framework/src/Illuminate/Foundation/Console/ConfigCacheCommand.php:80
     76▕             require $configPath;
     77▕         } catch (Throwable $e) {
     78▕             $this->files->delete($configPath);
     79▕
  ➜  80▕             throw new LogicException('Your configuration files are not serializable.', 0, $e);
     81▕         }
     82▕
     83▕         $this->info('Configuration cached successfully!');
     84▕     }

  1   /path/to/project/bootstrap/cache/config.php:<line-number>
      Error::("Call to undefined method App\Markdown\Renderers\BlockQuoteRenderer::__set_state()")

  2   /path/to/project/vendor/laravel/framework/src/Illuminate/Foundation/Console/ConfigCacheCommand.php:76
      require()

是什么原因造成的? ð

错误消息可能需要澄清。对于1,它说不可序列化。这可能会导致您相信config:cacheserialize()称为引擎盖下,但这不是。

它也没有指出 conform an an。

但是,在挖掘laravel/framework时,我们找到了this section of code

<?php
//...
        $configPath = $this->laravel->getCachedConfigPath();

        $this->files->put(
            $configPath, '<?php return '.var_export($config, true).';'.PHP_EOL
        );

        try {
            require $configPath;
        } catch (Throwable $e) {
          // ...
        }
// ...

在那里,打电话给var_export()。如果我们查看PHP manual以获取该功能,它显示了不同类型的输出的示例。

但专门查看示例#3:导出类。它具有此样本输出:

A::__set_state(array(
   'var' => 5,
))

您可能还记得,静态方法__set_state()出现在堆栈跟踪中。

如果我们读取PHP’s magic methods page,我们会看到这种静态方法允许将导出的对象字符串解析回对象中,同时自动向其提供其旧导出的数据!

和从laravel/framework重读该片段,现在发生的事情是有道理的。调用config:cache

  1. Laravel正在以完整的,生成的配置读取并从中生成合并的PHP文件。然后,
  2. 它立即在此预构建的PHP文件中读取,触发对该魔法方法的调用。

因此,似乎这个错误只是配置缓存方式的副作用。

那么我们如何解决这个问题? ð

我们可以为我们的配置中的新课程实现__set_state()return值也应该是该类的实例。

对于我的渲染器,我写了一个特质:

<?php

trait Resumeable
{
    public static function __set_state(array $state_array): static
    {
        $object = new static();

        foreach ($state_array as $prop => $state) {
            if (!property_exists($object, $prop)) continue;

            $object->{$prop} = $state;
        }

        return $object;
    }
}

我在PHP 8.1上测试了这一点。由于它是从班级中调用的,因此它也可以在privateprotected属性上工作。

也是一件好事,因为var_export()privateprotected属性导出数据。

摘要ð

您可能不需要这个。而且,如果您使用spatie/laravel-markdownPR#53确实很快合并了,因此您可以现在使用类字符串(感谢erikn69freekmurze!)

,这是解决您可能遇到的config缓存问题的一种方法。

感谢您的阅读!ð