Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
16 / 16
CRAP
100.00% covered (success)
100.00%
1 / 1
Module
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
16 / 16
57
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
12
 register
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 applyConfigMetadata
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 registerConfiguredAutoloaderPrefix
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
7
 applyConfigRoutes
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 applyConfigServices
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
4
 applyConfigEvents
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
7
 applyConfigMiddleware
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
5
 __set
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 __get
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 __isset
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 __unset
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 offsetSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetGet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetUnset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Module;
16
17use Pop\App;
18use Pop\Application;
19use Pop\Utils\Arr;
20use Pop\Utils\Helper;
21
22/**
23 * Pop module class
24 *
25 * @category   Pop
26 * @package    Pop\Module
27 * @author     Nick Sagona, III <nick@popphp.org>
28 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
29 * @license    https://www.popphp.org/license     New BSD License
30 * @version    5.0.0
31 * @property   mixed $config
32 */
33class Module extends AbstractModule implements \ArrayAccess
34{
35
36    /**
37     * Constructor
38     *
39     * Instantiate a module object
40     *
41     * Optional parameters are an application instance or a configuration object or array
42     */
43    public function __construct()
44    {
45        $args        = func_get_args();
46        $application = null;
47        $config      = null;
48        $name        = null;
49        $version     = null;
50
51        foreach ($args as $arg) {
52            if ($arg instanceof Application) {
53                $application = $arg;
54            } else if (is_array($arg) || ($arg instanceof \ArrayAccess)) {
55                $config = $arg;
56            } else if (preg_match('/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/', $arg)) {
57                $version = $arg;
58            } else if (is_string($arg)) {
59                $name = $arg;
60            }
61        }
62
63        if ($name !== null) {
64            $this->setName($name);
65        } else if ($this->name === null) {
66            $this->setName(str_replace('\\', '_', strtolower(get_called_class())));
67        }
68
69        if ($version !== null) {
70            $this->setVersion($version);
71        }
72
73        if ($config !== null) {
74            $this->registerConfig($config);
75        }
76
77        if ($application !== null) {
78            $this->register($application);
79        }
80    }
81
82    /**
83     * Register module
84     *
85     * @param  Application $application
86     * @throws Exception|\Pop\Service\Exception
87     * @return static
88     */
89    public function register(Application $application): static
90    {
91        $this->application = $application;
92
93        if ($this->config !== null) {
94            $this->applyConfigMetadata();
95            $this->registerConfiguredAutoloaderPrefix();
96            $this->applyConfigRoutes();
97            $this->applyConfigServices();
98            $this->applyConfigEvents();
99            $this->applyConfigMiddleware();
100        }
101
102        $this->application->modules->register($this);
103
104        return $this;
105    }
106
107    /**
108     * Set the module name and version from config, if available
109     *
110     * @return void
111     */
112    protected function applyConfigMetadata(): void
113    {
114        if (isset($this->config['name'])) {
115            $this->setName($this->config['name']);
116        }
117        if (!empty($this->config['version'])) {
118            $this->setVersion($this->config['version']);
119        }
120    }
121
122    /**
123     * If the autoloader is set and the module config has a defined prefix
124     * and src, register the module with the autoloader
125     *
126     * @return void
127     */
128    protected function registerConfiguredAutoloaderPrefix(): void
129    {
130        if (($this->application->autoloader() !== null) &&
131            isset($this->config['prefix']) && isset($this->config['src']) && file_exists($this->config['src'])
132        ) {
133            // Register as PSR-0
134            if (isset($this->config['psr-0']) && ($this->config['psr-0'])) {
135                $this->application->autoloader()->add($this->config['prefix'], $this->config['src']);
136            // Else, default to PSR-4
137            } else {
138                $this->application->autoloader()->addPsr4($this->config['prefix'], $this->config['src']);
139            }
140        }
141    }
142
143    /**
144     * If routes are set in the module config, register them with the application
145     *
146     * @return void
147     */
148    protected function applyConfigRoutes(): void
149    {
150        if (isset($this->config['routes']) && ($this->application->router() !== null)) {
151            $this->application->router()->addRoutes($this->config['routes']);
152        }
153    }
154
155    /**
156     * If services are set in the module config, register them with the application
157     *
158     * @return void
159     */
160    protected function applyConfigServices(): void
161    {
162        if (isset($this->config['services']) && ($this->application->services() !== null)) {
163            foreach ($this->config['services'] as $name => $service) {
164                $this->application->setService($name, $service);
165            }
166        }
167    }
168
169    /**
170     * If events are set in the module config, register them with the application
171     *
172     * @return void
173     */
174    protected function applyConfigEvents(): void
175    {
176        if (isset($this->config['events']) && ($this->application->events() !== null)) {
177            foreach ($this->config['events'] as $event) {
178                if (isset($event['name']) && isset($event['action'])) {
179                    $this->application->on(
180                        $event['name'],
181                        $event['action'],
182                        ((isset($event['priority'])) ? $event['priority'] : 0)
183                    );
184                }
185            }
186        }
187    }
188
189    /**
190     * If middleware is defined in the module config, register them with the application
191     *
192     * @return void
193     */
194    protected function applyConfigMiddleware(): void
195    {
196        $middlewareDisabled = App::middlewareDisabled();
197
198        if (isset($this->config['middleware']) && ($this->application->middleware() !== null) &&
199            (empty($middlewareDisabled) || ($middlewareDisabled == 'route'))) {
200            $this->application->middleware()->addItems(Arr::make($this->config['middleware']));
201        }
202    }
203
204    /**
205     * Set a pre-designated value in the module object
206     *
207     * @param  string $name
208     * @param  mixed  $value
209     * @return void
210     */
211    public function __set(string $name, mixed $value): void
212    {
213        if ($name == 'config') {
214            $this->registerConfig($value);
215        }
216    }
217
218    /**
219     * Get a pre-designated value from the module object
220     *
221     * @param  string $name
222     * @return mixed
223     */
224    public function __get(string $name): mixed
225    {
226        return match ($name) {
227            'config' => $this->config,
228            default  => null,
229        };
230    }
231
232    /**
233     * Determine if a pre-designated value in the module object exists
234     *
235     * @param  string $name
236     * @return bool
237     */
238    public function __isset(string $name): bool
239    {
240        return match ($name) {
241            'config' => ($this->config !== null),
242            default  => false,
243        };
244    }
245
246    /**
247     * Unset a pre-designated value in the module object
248     *
249     * @param  string $name
250     * @return void
251     */
252    public function __unset(mixed $name): void
253    {
254        if ($name == 'config') {
255            $this->config = null;
256        }
257    }
258
259    /**
260     * Set a value in the array
261     *
262     * @param  mixed $offset
263     * @param  mixed $value
264     * @return void
265     */
266    public function offsetSet(mixed $offset, mixed $value): void
267    {
268        $this->__set($offset, $value);
269    }
270
271    /**
272     * Get a value from the array
273     *
274     * @param  mixed $offset
275     * @return mixed
276     */
277    public function offsetGet(mixed $offset): mixed
278    {
279        return $this->__get($offset);
280    }
281
282    /**
283     * Determine if a value exists
284     *
285     * @param  mixed $offset
286     * @return bool
287     */
288    public function offsetExists(mixed $offset): bool
289    {
290        return $this->__isset($offset);
291    }
292
293    /**
294     * Unset a value from the array
295     *
296     * @param  mixed $offset
297     * @return void
298     */
299    public function offsetUnset(mixed $offset): void
300    {
301        $this->__unset($offset);
302    }
303
304}