Trong bài viết này, chúng ta sẽ tiến hành implement IoC Container.
1. Initalize project
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 📦phpioccontainer ┣ 📂src ┃ ┗ 📂Illuminate ┃ ┃ ┣ 📂Container ┃ ┃ ┃ ┗ 📜Container.php ┃ ┃ ┗ 📂Contracts ┃ ┃ ┃ ┗ 📂Container ┃ ┃ ┃ ┃ ┗ 📜Container.php ┣ 📂tests ┃ ┗ 📂Container ┃ ┃ ┗ 📜ContainerTest.php ┣ 📜composer.json ┗ 📜index.php |
Bên trên là cấu trúc thư mục khởi tạo project của chúng ta. Lần lượt nội dung của các file sẽ như sau :
composer.json
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"vscode/phpioccontainer"</span><span class="token punctuation">,</span> <span class="token property">"autoload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"psr-4"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"Illuminate\"</span><span class="token operator">:</span> <span class="token string">"src/Illuminate/"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"require-dev"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"phpunit/phpunit"</span><span class="token operator">:</span> <span class="token string">"^9"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
src/Illuminate/Container/Container.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">Illuminate<span class="token punctuation"></span>Container</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Contracts<span class="token punctuation"></span>Container<span class="token punctuation"></span>Container</span> <span class="token keyword">as</span> ContainerContract<span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Container</span> <span class="token keyword">implements</span> <span class="token class-name">ContainerContract</span> <span class="token punctuation">{</span> <span class="token keyword">protected</span> <span class="token variable">$bindings</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">bind</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">,</span> <span class="token variable">$concrete</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token single-quoted-string string">'bind'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token single-quoted-string string">'make'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
src/Illuminate/Contracts/Container/Container.php
1 2 3 4 5 6 7 8 9 10 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">Illuminate<span class="token punctuation"></span>Contracts<span class="token punctuation"></span>Container</span><span class="token punctuation">;</span> <span class="token keyword">interface</span> <span class="token class-name">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">bind</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">,</span> <span class="token variable">$concrete</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </span> |
tests/Container/ContainerTest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">Illuminate<span class="token punctuation"></span>Tests<span class="token punctuation"></span>Container</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Container<span class="token punctuation"></span>Container</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">PHPUnit<span class="token punctuation"></span>Framework<span class="token punctuation"></span>TestCase</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">ContainerTest</span> <span class="token keyword">extends</span> <span class="token class-name">TestCase</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">testClosureResolution</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$container</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Container</span><span class="token punctuation">;</span> <span class="token variable">$container</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'name'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token single-quoted-string string">'You'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">assertSame</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'You'</span><span class="token punctuation">,</span> <span class="token variable">$container</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'name'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Đó là nội dung của 4 files chính của chúng ta trong bài viết lần này.
Nếu ta chạy unit test tại thời điểm này, chắc chắn là test fail (RED), và đây là điều chúng ta mong muốn, theo đúng tinh thần của TDD.
Công việc cần làm của chúng ta bây giờ là làm cho test pass (GREEN).
2. Implement Container
2.1. Closure resolution
Chúng ta sẽ implement dạng đơn giản nhất của container, là nó chỉ thực hiện resolve các closure (function) được binding. Về ý tưởng thì tương đối đơn giản.
bind this with that.
make that then give me this
Ý tưởng implement là ta sẽ có một mảng bindings[]
để lưu trữ tất cả các bindings.
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 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">Illuminate<span class="token punctuation"></span>Container</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Contracts<span class="token punctuation"></span>Container<span class="token punctuation"></span>Container</span> <span class="token keyword">as</span> ContainerContract<span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Container</span> <span class="token keyword">implements</span> <span class="token class-name">ContainerContract</span> <span class="token punctuation">{</span> <span class="token keyword">protected</span> <span class="token variable">$bindings</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">bind</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">,</span> <span class="token variable">$concrete</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">bindings</span><span class="token punctuation">[</span><span class="token variable">$abstract</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token variable">$concrete</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$concrete</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">getConcrete</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token variable">$concrete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">protected</span> <span class="token keyword">function</span> <span class="token function">getConcrete</span><span class="token punctuation">(</span><span class="token variable">$abstract</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">bindings</span><span class="token punctuation">[</span><span class="token variable">$abstract</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Việc thực hiện khá là đơn giản, giờ thì thử chạy lại unit test của chúng ta nào
1 2 3 4 5 6 7 8 9 10 | $ ./vendor/bin/phpunit tests PHPUnit 9.5.2 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 00:00.004, Memory: 4.00 MB OK (1 test, 1 assertion) |
Hurray! Passed!
2.1. Singleton
…To Be Continued!
Cảm ơn các bạn đã đọc bài viết, hẹn gặp lại ở bài viết tiếp theo, chúng ta sẽ tiến hành implement dạng singleton của binding nhé!