-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfigprovider-bootstrap-modern-php-applications.html.twig
More file actions
193 lines (141 loc) · 9.94 KB
/
Copy pathconfigprovider-bootstrap-modern-php-applications.html.twig
File metadata and controls
193 lines (141 loc) · 9.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
{% extends '@layout/default.html.twig' %}
{% block json_ld %} {{ include('@jsonld/architecture/configprovider-bootstrap-modern-php-applications.jsonld.twig') }} {% endblock %}
{% block title %}ConfigProvider - Bootstrap Modern PHP Applications{% endblock %}
{% block page_title %}
{{ include('@partial/title-section.html.twig', {
back_href: url('page::blog'),
back_label: 'Back to Blog',
badge: 'Architecture',
article: article,
}) }}
{% endblock %}
{% block content %}
<div class="wrap post-body-wrap">
<div class="post-layout">
{{ include('@partial/left-menu.html.twig') }}
<article class="article">
<div class="entry">
<p class="mb-3">In PHP, the <code>ConfigProvider</code> is a class that is part of an application's bootstrap process. <strong>It's a class or callable that returns configuration data telling the platform which middleware should run, in what order, and sometimes under what conditions.</strong></p>
<p class="mb-3">If you're talking specifically about the ConfigProvider in the Laminas/Mezzio ecosystem, it's literally an array of configuration, settings, or anything else your application needs.</p>
<h2><a href="#where-is-the-configprovider-used"></a>Where Is the ConfigProvider Used?</h2>
<p class="mb-3">Mezzio (formerly Zend Expressive), Laminas, Slim, the Dotkernel Headless Platform, or other middleware-based frameworks often have a <code>ConfigProvider</code> class. In Laminas/Mezzio specifically, each module or package may contain a <code>ConfigProvider</code> that returns:</p>
<ul><li>Middleware pipeline configuration.<ul><li>Middleware classes or service names.</li>
<li>Error-handling middleware, which should have the lowest priority.</li>
<li>Middleware groups or nested arrays.</li>
</ul>
</li>
<li>Dependency injection mappings.</li>
<li>Request Handlers.</li>
</ul>
<p class="mb-3">Example in Dotkernel, which is an approach similar to Laminas/Mezzio:</p>
<pre>class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => $this->getDependencies(),
'templates' => $this->getTemplates(),
];
}
public function getDependencies(): array
{
return ,
'invokables' => ,
];
}
public function getTemplates(): array
{
return ,
'error' => ,
];
}
}</pre>
<p class="mb-3">What each item above means:</p>
<ul><li><code>dependencies</code> is used by the dependency injector (like <a href="https://docs.mezzio.dev/mezzio/v3/features/container/laminas-servicemanager/" target="_blank" rel="noreferrer noopener">laminas-servicemanager</a>) to construct every requested service.<ul><li><code>factories</code> will have the factory build the service.</li>
<li><code>invokables</code> will use <code>new</code> directly.</li>
<li>You can also use <code>aliases</code> to redirect to another service name and <code>delegators</code> to wrap the original service.</li>
</ul>
</li>
<li><code>templates</code> defines the paths for the template files.</li>
</ul>
<h2><a href="#how-the-configprovider-works"></a>How the ConfigProvider works</h2>
<p class="mb-3">The ConfigProvider is automatically picked up by the framework during application bootstrap. Let's look at it step by step:</p>
<ul><li><strong>Merge the global configuration</strong> - All ConfigProviders are merged into one array.</li>
<li><strong>Read the configuration array</strong> - The call is similar to the below and expects an array of entries:</li>
</ul>
<pre>$config = $container->get('config') ?? [];</pre>
<ul><li><strong>Resolve item</strong> - <code>$app->pipe()</code> is called to resolve one of the below instances:<ul><li>Resolve the service name from the container</li>
<li>Wrap the middleware, if an array is provided</li>
<li>Call the closure or invokable object.</li>
</ul>
</li>
<li><strong>Handle errors</strong> - This middleware is the last one in the pipeline to make sure it handles any exceptions.</li>
<li><strong>Execute at runtime</strong> - <a href="https://docs.laminas.dev/laminas-stratigility/" target="_blank" rel="noreferrer noopener">Laminas Stratigility</a> iterates over the pipeline in the order it was registered.<ul><li>Each middleware can <strong>handle</strong> the request and return a response, or <strong>delegate</strong> execution to the next middleware in the pipeline, until a <code>ResponseInterface</code> is returned to the client.</li>
</ul>
</li>
</ul>
<p class="mb-3">Below you can see how Mezzio and Dotkernel merge and use ConfigProviders to build the middleware pipeline and dependencies.</p>
<figure><img src="{{ asset('uploads/article/' ~ article.id ~ '/ConfigProvider2.png') }}" alt=""> </figure>
<h2><a href="#benefits"></a>Benefits</h2>
<ul><li>Centralized setup – Instead of hardcoding bootstrap code, you declare it in a config provider so it's easy to read, change, or extend.</li>
<li>Modular – Each package can ship with its own config without interfering with others.</li>
<li>Container-friendly – It works well with frameworks using DI containers like Laminas ServiceManager, PHP-DI, or Pimple.</li>
<li>Standardized service definitions - It has consistent rules for object creation that are separate from business logic.</li>
<li>Auto-Discovery - In Laminas/Mezzio, the <a href="https://docs.laminas.dev/laminas-config-aggregator/" target="_blank" rel="noreferrer noopener">ConfigAggregator</a> automatically loads and merges all ConfigProviders.</li>
</ul>
<blockquote><p class="mb-3">Dotkernel is an exception to this rule: new ConfigProviders have to be added manually in <code>config/config.php</code>, because all the initial ConfigProviders required to install the applications are already injected.</p>
</blockquote>
<ul><li>Environment-agnostic - It returns an array that defines dev, test, or prod environments.</li>
<li>Testability - The consistent, central configuration promotes isolated (e.g. per-module) testing, easier swapping of dependencies and the assertion of pipeline setup (e.g. check if a config key is present).</li>
</ul>
<h2 id="user-content-faq">Frequently Asked Questions</h2>
<div class="faq-list">
<details class="acc" open>
<summary>What is a ConfigProvider in PHP? <span class="chev">+</span></summary>
<div class="acc-body">
<p class="mb-0">It is a class that is part of an application's bootstrap process: a class or callable that returns configuration data telling the platform which middleware should run, in what order, and sometimes under what conditions.</p>
</div>
</details>
<details class="acc">
<summary>What does the ConfigProvider return in the Laminas/Mezzio ecosystem? <span class="chev">+</span></summary>
<div class="acc-body">
<p class="mb-0">In the Laminas/Mezzio ecosystem, it's literally an array of configuration, settings, or anything else the application needs, and each module or package may contain its own ConfigProvider returning middleware pipeline configuration, dependency injection mappings, and request handlers.</p>
</div>
</details>
<details class="acc">
<summary>What is the difference between 'factories' and 'invokables' in the dependencies array? <span class="chev">+</span></summary>
<div class="acc-body">
<p class="mb-0"><code>factories</code> will have the factory build the service, while <code>invokables</code> will use <code>new</code> directly. You can also use <code>aliases</code> to redirect to another service name and <code>delegators</code> to wrap the original service.</p>
</div>
</details>
<details class="acc">
<summary>How does the ConfigProvider get used during application bootstrap? <span class="chev">+</span></summary>
<div class="acc-body">
<p class="mb-0">It is automatically picked up by the framework during bootstrap: all ConfigProviders are merged into one array, the configuration array is read (similar to <code>$config = $container->get('config') ?? [];</code>), each item is resolved via <code>$app->pipe()</code>, the error-handling middleware is placed last in the pipeline, and at runtime Laminas Stratigility iterates over the pipeline in the order it was registered.</p>
</div>
</details>
<details class="acc">
<summary>Are new ConfigProviders auto-discovered in Dotkernel? <span class="chev">+</span></summary>
<div class="acc-body">
<p class="mb-0">Dotkernel is an exception to the usual auto-discovery rule: new ConfigProviders have to be added manually in <code>config/config.php</code>, because all the initial ConfigProviders required to install the applications are already injected.</p>
</div>
</details>
<details class="acc">
<summary>What are the benefits of using a ConfigProvider? <span class="chev">+</span></summary>
<div class="acc-body">
<p class="mb-0">Benefits include centralized setup instead of hardcoded bootstrap code, modularity so each package can ship its own config, container-friendliness with DI containers like Laminas ServiceManager, PHP-DI or Pimple, standardized service definitions, environment-agnostic configuration for dev/test/prod, and better testability of the pipeline setup.</p>
</div>
</details>
</div>
<h2><a href="#additional-resources"></a>Additional Resources</h2>
<ul><li><a href="https://docs.mezzio.dev/mezzio/v3/features/container/config/" target="_blank" rel="noreferrer noopener">Mezzio Container</a></li>
<li><a href="https://docs.laminas.dev/laminas-config-aggregator/config-providers/" target="_blank" rel="noreferrer noopener">Laminas Config Aggregator</a></li>
<li><a href="https://www.php-fig.org/psr/psr-15/" target="_blank" rel="noreferrer noopener">PSR-15 (HTTP Server Request Handlers)</a></li>
</ul>
<p class="mb-3"></p>
</div>
{{ include('@partial/post-nav.html.twig') }}
</article>
</div>
</div>
{% endblock %}