Symfony4如何自动引入目录下所有PHP类并实例化?
在Symfony 4中自动引入指定目录类并实例化的解决方案
嘿,我明白你遇到的问题了!你想循环实例化App\Service\Adapter目录下的所有类,但因为PHP找不到正确的命名空间,所以抛出了这个报错:
Attempted to load class "MyClass" from the global namespace. Did you forget a "use" statement for "App\Service\Adapter\MyClass"?
你尝试的几种方式都没命中要点——PHP不支持批量use类,直接加反斜杠也只是指向全局命名空间,根本没到你实际的类所在的命名空间。下面给你两种可行的解决办法:
你的原始代码参考
先贴一下你的代码方便对照:
<?php declare(strict_types = 1); namespace App\Service\Factory; use App\Service\Adapter\AdapterInterface; use Doctrine\ORM\EntityManagerInterface; use function dump; use function explode; use function scandir; /** * Class ArticleFactory * * @package App\Service\Factory */ class ArticleFactory { /** * @var EntityManagerInterface */ protected $entityManager; protected $pathService; /** * ArticleFactory constructor. * * @param EntityManagerInterface $entityManager * @param $pathService */ public function __construct(EntityManagerInterface $entityManager, $pathService) { $this->entityManager = $entityManager; $this->pathService = $pathService; } public function adapters(): void { $scandir = scandir($this->pathService . '/Adapter', SCANDIR_SORT_NONE); $adapters = []; foreach ($scandir as $key => $value) { if ($value !== '.' && $value !== '..' && $value !== 'AdapterAbstract.php' && $value !== 'AdapterInterface.php') { $cuttedString = explode('.php', $value); $className = $cuttedString[0]; $adapters[] = new $className($this->entityManager); } } foreach ($adapters as $adapter) { if ($adapter instanceof AdapterInterface) { $adapter->rss(); $article = $adapter->adapt(); foreach ($article as $item) { $this->entityManager->persist($item); } } } $this->entityManager->flush(); } }
你尝试过的无效方法
你之前试的这几种都解决不了问题:
use App\Service\Adapter;:这只是引入了Adapter命名空间本身,不是里面的具体类use App\Service\Adapter\*;:PHP不支持这种批量引入类的语法(仅对traits有类似支持,类不行)$className = '\\'.$cuttedString[0];:这样只是给类名加了全局命名空间的前缀,还是找不到App\Service\Adapter下的类
方案一:拼接完整命名空间(快速解决)
最直接的办法就是把类的完整命名空间前缀和文件名(类名)拼接起来,让PHP知道去哪里找这个类:
修改你循环里的类名拼接部分:
foreach ($scandir as $key => $value) { if ($value !== '.' && $value !== '..' && $value !== 'AdapterAbstract.php' && $value !== 'AdapterInterface.php') { $cuttedString = explode('.php', $value); // 拼接完整的命名空间 + 类名 $className = 'App\Service\Adapter\\' . $cuttedString[0]; $adapters[] = new $className($this->entityManager); } }
这样修改后,PHP就能正确定位到App\Service\Adapter目录下的类,不需要额外的use语句。
方案二:利用Symfony依赖注入(更推荐)
如果你的适配器类都实现了AdapterInterface,更符合Symfony最佳实践的方式是让框架自动帮你收集所有实现该接口的服务,不用自己手动扫描文件:
- 修改
ArticleFactory的构造函数,注入所有实现AdapterInterface的服务:
<?php declare(strict_types = 1); namespace App\Service\Factory; use App\Service\Adapter\AdapterInterface; use Doctrine\ORM\EntityManagerInterface; /** * Class ArticleFactory * * @package App\Service\Factory */ class ArticleFactory { /** * @var EntityManagerInterface */ protected $entityManager; /** @var AdapterInterface[] */ protected $adapters; /** * ArticleFactory constructor. * * @param EntityManagerInterface $entityManager * @param iterable $adapters 注入所有实现AdapterInterface的服务 */ public function __construct(EntityManagerInterface $entityManager, iterable $adapters) { $this->entityManager = $entityManager; $this->adapters = $adapters; } public function adapters(): void { // 直接遍历注入的适配器,不用自己扫描文件了 foreach ($this->adapters as $adapter) { $adapter->rss(); $article = $adapter->adapt(); foreach ($article as $item) { $this->entityManager->persist($item); } } $this->entityManager->flush(); } }
- 配置
services.yaml,让Symfony自动标记并收集这些服务:
打开config/services.yaml,添加以下配置:
services: # 自动给所有实现AdapterInterface的类打上标签 _instanceof: App\Service\Adapter\AdapterInterface: tags: ['app.adapter'] # 给ArticleFactory注入所有带app.adapter标签的服务 App\Service\Factory\ArticleFactory: arguments: $adapters: !tagged_iterator app.adapter
这种方式的好处是不需要自己处理文件系统,Symfony会自动帮你管理所有适配器的依赖,比如如果某个适配器需要其他服务,框架也会自动注入,更符合Symfony的依赖注入理念。
内容的提问来源于stack exchange,提问作者Valentin Harrang
相关产品推荐
相关产品推荐

