33
44namespace Arkitect \RuleBuilders \Architecture ;
55
6- use Arkitect \Expression \Boolean \Andx ;
7- use Arkitect \Expression \Boolean \Not ;
6+ use Arkitect \Expression \Boolean \Orx ;
87use Arkitect \Expression \Expression ;
9- use Arkitect \Expression \ForClasses \DependsOnlyOnTheseNamespaces ;
10- use Arkitect \Expression \ForClasses \NotDependsOnTheseNamespaces ;
8+ use Arkitect \Expression \ForClasses \DependsOnlyOnTheseExpressions ;
119use Arkitect \Expression \ForClasses \ResideInOneOfTheseNamespaces ;
1210use Arkitect \Rules \Rule ;
1311
1412class Architecture implements Component, DefinedBy, Where, MayDependOnComponents, MayDependOnAnyComponent, ShouldNotDependOnAnyComponent, ShouldOnlyDependOnComponents, Rules
1513{
1614 /** @var string */
1715 private $ componentName ;
18- /** @var array<string, string> */
16+ /** @var array<string, Expression| string> */
1917 private $ componentSelectors ;
2018 /** @var array<string, string[]> */
2119 private $ allowedDependencies ;
2220 /** @var array<string, string[]> */
23- private $ componentDependsOnlyOnTheseNamespaces ;
21+ private $ componentDependsOnlyOnTheseComponents ;
2422
2523 private function __construct ()
2624 {
2725 $ this ->componentName = '' ;
2826 $ this ->componentSelectors = [];
2927 $ this ->allowedDependencies = [];
30- $ this ->componentDependsOnlyOnTheseNamespaces = [];
28+ $ this ->componentDependsOnlyOnTheseComponents = [];
3129 }
3230
3331 public static function withComponents (): Component
@@ -72,7 +70,7 @@ public function shouldNotDependOnAnyComponent()
7270
7371 public function shouldOnlyDependOnComponents (string ...$ componentNames )
7472 {
75- $ this ->componentDependsOnlyOnTheseNamespaces [$ this ->componentName ] = $ componentNames ;
73+ $ this ->componentDependsOnlyOnTheseComponents [$ this ->componentName ] = $ componentNames ;
7674
7775 return $ this ;
7876 }
@@ -93,63 +91,61 @@ public function mayDependOnAnyComponent()
9391
9492 public function rules (): iterable
9593 {
96- $ layerNames = array_keys ($ this ->componentSelectors );
97-
9894 foreach ($ this ->componentSelectors as $ name => $ selector ) {
9995 if (isset ($ this ->allowedDependencies [$ name ])) {
100- $ forbiddenComponents = array_diff ($ layerNames , [$ name ], $ this ->allowedDependencies [$ name ]);
101-
102- if (!empty ($ forbiddenComponents )) {
103- yield Rule::allClasses ()
104- ->that (\is_string ($ selector ) ? new ResideInOneOfTheseNamespaces ($ selector ) : $ selector )
105- ->should ($ this ->createForbiddenExpression ($ forbiddenComponents ))
106- ->because ('of component architecture ' );
107- }
96+ yield Rule::allClasses ()
97+ ->that (\is_string ($ selector ) ? new ResideInOneOfTheseNamespaces ($ selector ) : $ selector )
98+ ->should ($ this ->createAllowedExpression (
99+ array_merge ([$ name ], $ this ->allowedDependencies [$ name ])
100+ ))
101+ ->because ('of component architecture ' );
108102 }
109103
110- if (!isset ($ this ->componentDependsOnlyOnTheseNamespaces [$ name ])) {
111- continue ;
104+ if (isset ($ this ->componentDependsOnlyOnTheseComponents [$ name ])) {
105+ yield Rule::allClasses ()
106+ ->that (\is_string ($ selector ) ? new ResideInOneOfTheseNamespaces ($ selector ) : $ selector )
107+ ->should ($ this ->createAllowedExpression ($ this ->componentDependsOnlyOnTheseComponents [$ name ]))
108+ ->because ('of component architecture ' );
112109 }
110+ }
111+ }
113112
114- $ allowedDependencies = array_map (function (string $ componentName ): string {
115- return $ this ->componentSelectors [$ componentName ];
116- }, $ this ->componentDependsOnlyOnTheseNamespaces [$ name ]);
113+ private function createAllowedExpression (array $ components ): Expression
114+ {
115+ $ namespaceSelectors = $ this ->extractComponentsNamespaceSelectors ($ components );
116+
117+ $ expressionSelectors = $ this ->extractComponentExpressionSelectors ($ components );
118+
119+ if ([] === $ namespaceSelectors && [] === $ expressionSelectors ) {
120+ return new Orx (); // always true
121+ }
117122
118- yield Rule::allClasses ()
119- ->that (new ResideInOneOfTheseNamespaces ($ selector ))
120- ->should (new DependsOnlyOnTheseNamespaces (...$ allowedDependencies ))
121- ->because ('of component architecture ' );
123+ if ([] !== $ namespaceSelectors ) {
124+ $ expressionSelectors [] = new ResideInOneOfTheseNamespaces (...$ namespaceSelectors );
122125 }
126+
127+ return new DependsOnlyOnTheseExpressions (...$ expressionSelectors );
123128 }
124129
125- public function createForbiddenExpression (array $ forbiddenComponents ): Expression
130+ private function extractComponentsNamespaceSelectors (array $ components ): array
126131 {
127- $ forbiddenNamespaceSelectors = array_filter (
132+ return array_filter (
128133 array_map (function (string $ componentName ): ?string {
129134 $ selector = $ this ->componentSelectors [$ componentName ];
130135
131136 return \is_string ($ selector ) ? $ selector : null ;
132- }, $ forbiddenComponents )
137+ }, $ components )
133138 );
139+ }
134140
135- $ forbiddenExpressionSelectors = array_filter (
141+ private function extractComponentExpressionSelectors (array $ components ): array
142+ {
143+ return array_filter (
136144 array_map (function (string $ componentName ): ?Expression {
137145 $ selector = $ this ->componentSelectors [$ componentName ];
138146
139147 return \is_string ($ selector ) ? null : $ selector ;
140- }, $ forbiddenComponents )
148+ }, $ components )
141149 );
142-
143- $ forbiddenExpressionList = [];
144- if ([] !== $ forbiddenNamespaceSelectors ) {
145- $ forbiddenExpressionList [] = new NotDependsOnTheseNamespaces (...$ forbiddenNamespaceSelectors );
146- }
147- if ([] !== $ forbiddenExpressionSelectors ) {
148- $ forbiddenExpressionList [] = new Not (new Andx (...$ forbiddenExpressionSelectors ));
149- }
150-
151- return 1 === \count ($ forbiddenExpressionList )
152- ? array_pop ($ forbiddenExpressionList )
153- : new Andx (...$ forbiddenExpressionList );
154150 }
155151}
0 commit comments