j6s / phparch
Requires
- php: ^7.4|^8.0
- nikic/php-parser: ^4.0
- phpdocumentor/reflection-docblock: ^5.2.2
- phpdocumentor/type-resolver: ^1.4
- symfony/finder: ^4.1|^5.0|^6.0
- thecodingmachine/safe: ^1.3|^2.0
Requires (Dev)
- phpstan/phpstan: ^1.5.1
- phpunit/phpunit: ^9.4
- squizlabs/php_codesniffer: ^3.5
Suggests
- phpunit/phpunit: For assertion helpers to work correctly.
This package is auto-updated.
Last update: 2022-11-20 12:12:13 UTC
README
本项目已不再维护。在我开始开发时,类似的工具尚未存在,或者在 deptrac 的情况下,没有覆盖我的用例。如今,更好的工具已经存在,我很乐意将用户引向那些工具。
如果之前使用过 PHPArch,两者都可以作为良好的替代品。
PHPArch 
这是什么?
PHPArch 是一个 PHP 项目的架构测试库,仍在开发中。它受到 archlint (C#) 和 archunit (java) 的启发。
它可用于帮助强制执行应用程序中的架构边界,以防止随着时间的推移,通过在先前定义良好的架构边界之间引入依赖项而导致架构恶化。
安装
您可以使用 composer 安装 PHPArch。如果您不知道 composer 是什么,那么您可能不需要架构测试库。
$ composer require j6s/phparch
简单的命名空间验证
PHPArch 可以帮助您完成的 simplest 检查类型是简单的基于命名空间的检查:为哪些命名空间可以或不可以依赖于哪些其他命名空间设置规则。
public function testSimpleNamespaces() { (new PhpArch()) ->fromDirectory(__DIR__ . '/../../app') ->validate(new ForbiddenDependency('Lib\\', 'App\\')) ->validate(new MustBeSelfContained('App\\Utility')) ->validate(new MustOnlyDependOn('App\\Mailing', 'PHPMailer\\PHPMailer')) ->assertHasNoErrors(); }
可用的验证器
目前有以下验证器可用
ForbiddenDependency
允许您声明一个命名空间不允许依赖于另一个命名空间。MustBeSelfContained
允许您声明一个命名空间必须是自包含的,这意味着它可能没有任何外部依赖项。MustOnlyDependOn
允许您声明一个命名空间必须只依赖于另一个命名空间。MustOnlyHaveAutoloadableDependencies
检查所有依赖项是否可以在当前环境中自动加载。如果两个软件包不应该有任何相互依赖,但它们仍然悄悄地进入,因为软件包经常一起使用,这可能会很有帮助。AllowInterfaces
是一个包装器,用于允许依赖项如果它们是接口。MustOnlyDependOnComposerDependencies
检查给定命名空间中的所有依赖是否都在给定的composer.json
文件中声明。这有助于防止意外依赖,如果某个仓库包含多个包。ExplicitlyAllowDependency
是一个包装器,允许特定的依赖。
大多数架构边界都可以用这些规则来描述。
定义架构
PHPArch 还包含一个流畅的 API,允许您定义基于组件的架构,然后进行验证。该 API 基于组件,组件通过一个或多个命名空间来识别,而不是层或“洋葱皮”,因为这是最简单的方式来传达任何架构——无论其实施细节如何。
public function testArchitecture() { $architecture = (new Architecture()) ->component('Components')->identifiedByNamespace('J6s\\PhpArch\\Component') ->mustNotDependOn('Validation')->identifiedByNamespace('J6s\\PhpArch\\Validation'); (new PhpArch()) ->fromDirectory(__DIR__ . '/../../app') ->validate($architecture) ->assertHasNoErrors(); }
定义架构的大部分内容只是对上述命名空间验证器之上的语法糖。以下方法允许您向您的组件结构添加断言
mustNotDependOn
mustNotDependDirectlyOn
mustNotBeDependedOnBy
mustOnlyDependOn
mustNotDependOnAnyOtherComponent
mustOnlyDependOnComposerDependencies
dissallowInterdependence
isAllowedToDependOn
语法糖:组件的批量定义
虽然用于定义架构的口语 API 很好,但如果您有很多组件,它可能会变得复杂且难以阅读。可以使用 components
方法使用简单的关联数组来定义组件,其中键是组件名称,值是定义组件的命名空间。这样,组件的定义和设置依赖规则可以分成两个步骤,以提高可读性。
// This $architecture->components([ 'Foo' => 'Vendor\\Foo', 'Bar' => [ 'Vendor\\Bar', 'Vendor\\Deep\\Bar' ] ]); // Is the same as this $architecture->component('Foo') ->identifiedByNamespace('Vendor\\Foo') ->component('Bar') ->identifierByNamespace('Vendor\\Bar') ->identifiedByNamespace('Vendor\\Deep\\Bar')
语法糖:链式多个依赖规则
如果在这些方法之一中引用了不存在的组件,它将被创建。这些方法还将引用的组件设置为当前活动组件——因此,当使用 ->mustNotDependOn('FooBar')
时,所有后续操作都将引用 FooBar
组件。
为了为单个组件链式多个依赖规则,有一些便利方法可用
andMustNotDependOn
andMustNotBeDependedOnBy
// This (new Architecture) ->component('Foo') ->mustNotDependOn('Bar') ->andMustNotDependOn('Baz') // Is this same as this: (new Architecture()) ->component('Foo')->mustNotDependOn('Bar') ->component('Foo')->mustNotDependOn('Baz')
针对 monorepos 的简写:addComposerBasedComponent
如果一个仓库包含多个包,每个包都有自己的 composer.json
文件,那么很容易意外使用不属于当前包的 composer.json
文件中的方法或类。
为了防止这种情况,可以使用 Architecture->mustOnlyDependOnComposerDependencies
方法和 MustOnlyDependOnComposerDependencies
验证器来检查是否所有使用的命名空间都在给定的 composer.json
文件中声明
$architecture = (new Architecture) ->component('vendor/subpackage') ->identifierByNamespace('Vendor\\Subpackage\\') ->mustOnlyDependOnComposerDependencies('packages/subpackage/composer.json');
然而,composer.json
已经包含了有关包名称和命名空间的信息。因此,可以使用 addComposerBasedComponent
方法来简化操作
$architecture = (new Architecture) ->addComposerBasedComponent('packages/subpackage/composer.json');