Issues (2884)

src/Di/Traits/RegisterTrait.php (2 issues)

1
<?php
2
3
namespace Jaxon\Di\Traits;
4
5
use Jaxon\App\CallableClass;
6
use Jaxon\App\Config\ConfigManager;
7
use Jaxon\App\Dialog\Library\DialogLibraryManager;
8
use Jaxon\App\I18n\Translator;
9
use Jaxon\App\View\ViewRenderer;
10
use Jaxon\Exception\SetupException;
11
use Jaxon\Plugin\AnnotationReaderInterface;
12
use Jaxon\Plugin\Request\CallableClass\CallableClassHelper;
13
use Jaxon\Plugin\Request\CallableClass\CallableObject;
14
use Jaxon\Plugin\Request\CallableClass\CallableRepository;
15
use Jaxon\Request\Call\Paginator;
16
use Jaxon\Request\Factory\Factory;
17
use Jaxon\Request\Factory\RequestFactory;
18
use Jaxon\Request\Handler\CallbackManager;
19
use Jaxon\Request\Target;
20
use Jaxon\Utils\Config\Config;
21
use ReflectionClass;
22
use ReflectionException;
23
24
use function call_user_func;
25
26
trait RegisterTrait
27
{
28
    /**
29
     * @param mixed $xRegisteredObject
30
     * @param array $aDiOptions
31
     *
32
     * @return void
33
     */
34
    private function setDiAttributes($xRegisteredObject, array $aDiOptions)
35
    {
36
        foreach($aDiOptions as $sName => $sClass)
37
        {
38
            // Set the protected attributes of the object
39
            $cSetter = function($xInjectedObject) use($sName) {
40
                // Warning: dynamic properties will be deprecated in PHP8.2.
41
                $this->$sName = $xInjectedObject;
42
            };
43
            // Can now access protected attributes
44
            call_user_func($cSetter->bindTo($xRegisteredObject, $xRegisteredObject), $this->get($sClass));
45
        }
46
    }
47
48
    /**
49
     * Get a callable object when one of its method needs to be called
50
     *
51
     * @param CallableObject $xCallableObject
52
     * @param Target $xTarget
53
     *
54
     * @return mixed
55
     */
56
    public function getRegisteredObject(CallableObject $xCallableObject, Target $xTarget)
57
    {
58
        // Set attributes from the DI container.
59
        // The class level DI options were set when creating the object instance.
60
        // We now need to set the method level DI options.
61
        $aDiOptions = $xCallableObject->getMethodDiOptions($xTarget->getMethodName());
62
        $xRegisteredObject = $this->g($xCallableObject->getClassName());
63
        $this->setDiAttributes($xRegisteredObject, $aDiOptions);
64
65
        // Set the Jaxon request target in the helper
66
        if($xRegisteredObject instanceof CallableClass)
67
        {
68
            // Set the protected attributes of the object
69
            $cSetter = function() use($xTarget) {
70
                $this->xCallableClassHelper->xTarget = $xTarget;
71
            };
72
            // Can now access protected attributes
73
            call_user_func($cSetter->bindTo($xRegisteredObject, $xRegisteredObject));
74
        }
75
        return $xRegisteredObject;
76
    }
77
78
    /**
79
     * Register a callable class
80
     *
81
     * @param string $sClassName The callable class name
82
     * @param array $aOptions The callable object options
83
     *
84
     * @return void
85
     * @throws SetupException
86
     */
87
    public function registerCallableClass(string $sClassName, array $aOptions)
88
    {
89
        $sRequestFactory = $sClassName . '_RequestFactory';
90
        $sCallableObject = $sClassName . '_CallableObject';
91
        $sReflectionClass = $sClassName . '_ReflectionClass';
92
93
        // Prevent duplication
94
        if($this->h($sReflectionClass)) // It's important not to use the class name here.
95
        {
96
            return;
97
        }
98
99
        // Make sure the registered class exists
100
        if(isset($aOptions['include']))
101
        {
102
            require_once($aOptions['include']);
103
        }
104
        // Register the reflection class
105
        try
106
        {
107
            $this->val($sReflectionClass, new ReflectionClass($sClassName));
108
        }
109
        catch(ReflectionException $e)
110
        {
111
            $xTranslator = $this->g(Translator::class);
112
            $sMessage = $xTranslator->trans('errors.class.invalid', ['name' => $sClassName]);
113
            throw new SetupException($sMessage);
114
        }
115
116
        // Register the callable object
117
        $this->set($sCallableObject, function($di) use($sReflectionClass, $sClassName, $aOptions) {
118
            $xRepository = $di->g(CallableRepository::class);
119
            $aProtectedMethods = $xRepository->getProtectedMethods($sClassName);
120
            return new CallableObject($di, $di->g(AnnotationReaderInterface::class),
121
                $di->g($sReflectionClass), $aOptions, $aProtectedMethods);
122
        });
123
124
        // Register the request factory
125
        $this->set($sRequestFactory, function($di) use($sCallableObject) {
126
            $xConfigManager = $di->g(ConfigManager::class);
127
            $xCallable = $di->g($sCallableObject);
128
            $sJsClass = $xConfigManager->getOption('core.prefix.class') . $xCallable->getJsName() . '.';
129
            return new RequestFactory($sJsClass, $di->g(DialogLibraryManager::class), $di->g(Paginator::class));
130
        });
131
132
        // Register the user class, but only if the user didn't already.
133
        if(!$this->h($sClassName))
134
        {
135
            $this->set($sClassName, function($di) use($sReflectionClass) {
136
                return $this->make($di->g($sReflectionClass));
137
            });
138
        }
139
        // Initialize the user class instance
140
        $this->xLibContainer->extend($sClassName, function($xRegisteredObject)
141
            use($sCallableObject, $sClassName) {
142
            if($xRegisteredObject instanceof CallableClass)
143
            {
144
                $cSetter = function($di) use($sClassName) {
145
                    // Set the protected attributes of the object
146
                    $this->xCallableClassHelper = new CallableClassHelper($di, $sClassName);
0 ignored issues
show
Bug Best Practice introduced by
The property xCallableClassHelper does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
147
                    $this->response = $di->getResponse();
0 ignored issues
show
Bug Best Practice introduced by
The property response does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
148
                };
149
                // Can now access protected attributes
150
                call_user_func($cSetter->bindTo($xRegisteredObject, $xRegisteredObject), $this);
151
            }
152
153
            // Run the callbacks for class initialisation
154
            $this->g(CallbackManager::class)->onInit($xRegisteredObject);
155
156
            // Set attributes from the DI container.
157
            // The class level DI options are set when creating the object instance.
158
            // The method level DI options are set only when calling the method in the ajax request.
159
            /** @var CallableObject */
160
            $xCallableObject = $this->g($sCallableObject);
161
            $this->setDiAttributes($xRegisteredObject, $xCallableObject->getClassDiOptions());
162
163
            return $xRegisteredObject;
164
        });
165
    }
166
167
    /**
168
     * Get the callable object for a given class
169
     *
170
     * @param string $sClassName
171
     *
172
     * @return CallableObject
173
     */
174
    public function getCallableObject(string $sClassName): CallableObject
175
    {
176
        return $this->g($sClassName . '_CallableObject');
177
    }
178
179
    /**
180
     * Get the request factory for a given class
181
     *
182
     * @param string $sClassName
183
     *
184
     * @return RequestFactory
185
     */
186
    public function getRequestFactory(string $sClassName): RequestFactory
187
    {
188
        return $this->g($sClassName . '_RequestFactory');
189
    }
190
191
    /**
192
     * @param string $sClassName    The package class name
193
     *
194
     * @return string
195
     */
196
    private function getPackageConfigKey(string $sClassName): string
197
    {
198
        return $sClassName . '_config';
199
    }
200
201
    /**
202
     * Register a package
203
     *
204
     * @param string $sClassName    The package class name
205
     * @param Config $xPkgConfig    The user provided package options
206
     *
207
     * @return void
208
     * @throws SetupException
209
     */
210
    public function registerPackage(string $sClassName, Config $xPkgConfig)
211
    {
212
        $sPkgConfigKey = $this->getPackageConfigKey($sClassName);
213
        $this->val($sPkgConfigKey, $xPkgConfig);
214
        // Register the user class, but only if the user didn't already.
215
        if(!$this->h($sClassName))
216
        {
217
            $this->set($sClassName, function() use($sClassName) {
218
                return $this->make($sClassName);
219
            });
220
        }
221
        $this->xLibContainer->extend($sClassName, function($xPackage) use($sPkgConfigKey) {
222
            $di = $this;
223
            $cSetter = function() use($di, $sPkgConfigKey) {
224
                // Set the protected attributes of the object
225
                $this->_init($di->g($sPkgConfigKey), $di->g(Factory::class), $di->g(ViewRenderer::class));
226
                $this->init();
227
            };
228
            // Can now access protected attributes
229
            call_user_func($cSetter->bindTo($xPackage, $xPackage));
230
            return $xPackage;
231
        });
232
    }
233
234
    /**
235
     * Get the config of a package
236
     *
237
     * @param string $sClassName    The package class name
238
     *
239
     * @return Config
240
     */
241
    public function getPackageConfig(string $sClassName): Config
242
    {
243
        return $this->g($this->getPackageConfigKey($sClassName));
244
    }
245
}
246