get($key); } /** * @param string $key * @return mixed */ public function get($key) { return (isset($this->instances[$key]) ? $this->instances[$key] : $this->engine->checkRegistry($key)); } /** * @param string $key * @param mixed $value * @return void */ public function set($key, $value) { $this->instances[$key] = $value; } /** * @param string $key * @return bool */ public function has($key) { return isset($this->instances[$key]); } /** * UnSet * * Unsets registry value by key. * * @param string $key * @return void */ public function delete(string $key) { if (isset($this->instances[$key])) { unset($this->instances[$key]); } } /** * Try to obtain an iniciated engine instance * * @param string|object|null $class (optional) * @param array $args (optional) * @param bool $onlyCheckInstances (Optional) if true, don't create an auto-instance * * @return \Phacil\Framework\Registry|object|null * @since 2.0.0 */ static public function getInstance($class = null, $args = [], $onlyCheckInstances = false) { if(!$class) return \Phacil\Framework\startEngineExacTI::getRegistry(); $registry = \Phacil\Framework\startEngineExacTI::getRegistry(); $return = false; $classObj = (is_object($class)) ? get_class($class) : $class; if (isset(self::$autoInstances[($classObj)])) return self::$autoInstances[($classObj)]; foreach ($registry->instances as $value) { if(!is_object($value)) continue; if(get_class($value) == $classObj) {$return = $value; break; } } if($return) return $return; if($onlyCheckInstances) return $return; if(is_string($class)) { $classCreate = self::checkPreference($class); return self::getInstance()->injectionClass($classCreate, $args, true); } if (is_object($class)) { return self::setAutoInstance($class); } return null; } /** * Force the creation of an instance of a class, even if it already exists. * * @param string $class Class full qualified name * @param array $args (Optional) An array with the arguments to pass to the constructor * @return mixed * @throws \Phacil\Framework\Exception\ReflectionException * @throws \Phacil\Framework\Exception */ public function create($class, $args = array()) { if(is_string($class)) { return $this->injectionClass($class, $args, true); } return null; } /** * Adds DI preferences from a JSON file. * * This method reads a JSON file containing preferences and merges them into the existing preferences array. * * @param string $jsonFilePath The path to the JSON file containing preferences. * @return void * @throws \Phacil\Framework\Exception If there is an error reading the JSON file or if the JSON data is invalid. */ static public function addPreference($jsonFilePath) { $dataArray = []; if (file_exists($jsonFilePath) && $jsonData = file_get_contents($jsonFilePath)) { $dataArray = json_decode($jsonData, true); // Verifica se a conversão foi bem-sucedida if ($dataArray === null && json_last_error() !== JSON_ERROR_NONE) { // Se houver um erro na conversão, trata o erro $error = new \Phacil\Framework\Exception\Error(sprintf("DI preference %s error: ", $jsonFilePath). json_last_error_msg()); $error->setSeverity(E_PARSE); return; } } /* if (isset($dataArray['preferences'])) self::$preferences = array_merge(self::$preferences, $dataArray['preferences']); */ if(!empty($dataArray)){ self::$preferences = self::array_merge_recursive_distinct(self::$preferences, $dataArray); } /* if(count($dataArray) >= 1) { foreach($dataArray as $key => $value) { if($key !== "preferences") self::$diOptions[$key] = isset(self::$diOptions[$key]) ? self::array_merge_recursive_distinct(self::$diOptions[$key], $value) : $value; } } */ } private static function array_merge_recursive_distinct(array $array1, array $array2) { $merged = $array1; foreach ($array2 as $key => &$value) { if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) { if (is_numeric(self::array_key_first($value))) { // Concatenate arrays if both are numeric arrays (lists) $merged[$key] = array_merge($merged[$key], $value); } else { // Recursively merge associative arrays $merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value); } } else { $merged[$key] = $value; } } return $merged; } private static function array_key_first($arr){ if (!function_exists('array_key_first')) { foreach ($arr as $key => $unused) { return $key; } return null; } else { return array_key_first($arr); } } /** * Adds DI preferences from a JSON file. * * This method reads a JSON file containing preferences and merges them into the existing preferences array. * * @param string $jsonFilePath The path to the JSON file containing preferences. * @return void * @throws \Phacil\Framework\Exception If there is an error reading the JSON file or if the JSON data is invalid. */ static public function addPreferenceByRoute($route, $levels = 2) { $routeArray = explode('/', $route); for ($i=1; $i <= $levels; $i++) { # code... if($i >= count($routeArray)) break; $routeGlue = implode("/", array_slice($routeArray, 0, $i)); self::addDIrouteChecked($routeGlue); $directory = \Phacil\Framework\Config::DIR_APP_MODULAR() . self::case_insensitive_pattern($routeGlue) . '/etc/preferences.json'; $files = glob($directory, GLOB_MARK); if(!empty($files)) break; } if(isset($files[0])) { $jsonFilePath = $files[0]; } else { return; } if (file_exists($jsonFilePath)) { self::addPreference($jsonFilePath); } } /** * checks if a route has already had the DI checked * @param string $route * @return bool */ static public function isDIrouteChecked($route){ $route = strtolower($route); return isset(self::$diCheckedRoutes[$route]); } /** * Add has check DI route * @param string $route * @return true */ static public function addDIrouteChecked($route){ $route = strtolower($route); self::$diCheckedRoutes[$route] = true; return self::$diCheckedRoutes[$route]; } /** * * @param string $for * @param string $to * @return void */ static public function addDIPreference($for, $to) { self::$preferences[$for]['preference'] = $to; } /** * Check if preference for a class exists * * @param string $for * @return bool */ static public function checkPreferenceExist($for) { return isset(self::$preferences[$for]) ? (isset(self::$preferences[$for]['preference']) ?: is_string(self::$preferences[$for])) : false; } /** * Check if DI preferences exists * * @param string $for * @return bool */ public static function havePreferences($class){ return isset(self::$preferences[$class]); } /** * * @param string $class * @return array|null */ public static function getClassPreferences($class) { return isset(self::$preferences[$class]) ? self::$preferences[$class] : null; } /** * Generate Glob case insensitive pattern * @param string $string * @return string */ static public function case_insensitive_pattern($string) { $pattern = ''; foreach (str_split($string) as $char) { if (ctype_alpha($char)) { // se o caractere é uma letra $pattern .= '[' . strtoupper($char) . strtolower($char) . ']'; // adiciona as duas variações de caixa } else { $pattern .= $char; // mantém o caractere original } } return $pattern; } /** * Checks if a preference has been set for the specified class and returns it if found. * * This method checks if a preference has been set for the specified class. If a preference * is found, it returns the class name defined as the preference; otherwise, it returns the * original class name. * * @param string $class The class name to check for a preference. * @return string The class name defined as the preference, if found; otherwise, the original class name. */ static public function checkPreference($class) { if(isset(self::$preferences[$class])) { if(is_array(self::$preferences[$class]) && isset(self::$preferences[$class]['preference'])){ return self::$preferences[$class]['preference']; } elseif(is_array(self::$preferences[$class]) && !isset(self::$preferences[$class]['preference'])) { return $class; } return self::$preferences[$class]; } return $class; } /** * Check argument type to inject * * @param string $class * @param string $argument * @return mixed * @throws \ReflectionException * @throws \Exception * @throws \Phacil\Framework\Exception */ protected static function checkArgumentType($class, $argument) { if(self::havePreferences($class) && is_array(self::$preferences[$class])){ if(isset(self::$preferences[$class]['arguments']) && isset(self::$preferences[$class]['arguments'][$argument])){ //Check if is an array for determine configurations if(!is_array(self::$preferences[$class]['arguments'][$argument])) return self::$preferences[$class]['arguments'][$argument]; if(isset(self::$preferences[$class]['arguments'][$argument]['type']) && isset(self::$preferences[$class]['arguments'][$argument]['value'])){ return self::returnArgumentType(self::$preferences[$class]['arguments'][$argument]['type'], self::$preferences[$class]['arguments'][$argument]['value']); } if (isset(self::$preferences[$class]['arguments'][$argument]['objects'])) { return self::returnArgumentType('object[]', self::$preferences[$class]['arguments'][$argument]['objects']); } if (is_array(self::$preferences[$class]['arguments'][$argument]) && count(self::$preferences[$class]['arguments'][$argument]) === 1) { $type = key(self::$preferences[$class]['arguments'][$argument]); // Verifica se $type é um tipo válido $validTypes = ['string', 'int', 'integer', 'bool', 'boolean', 'float', 'double', 'array', 'object', 'objects', 'object[]', 'config']; if (in_array($type, $validTypes, true)) { return self::returnArgumentType($type, reset(self::$preferences[$class]['arguments'][$argument])); } } return self::$preferences[$class]['arguments'][$argument]; } } return null; } /** * Help argument type * * @param string $type * @param mixed $argumentValue * @return mixed * @throws \ReflectionException * @throws \Exception * @throws \Phacil\Framework\Exception */ private static function returnArgumentType($type, $argumentValue) { switch ($type) { case 'object': return self::getInstance($argumentValue); break; case 'object[]': $object = array_map(function($value){ return self::getInstance($value); }, $argumentValue); return $object; break; case 'bool': case 'boolean': return boolval($argumentValue); break; case 'integer': case 'int': return intval($argumentValue); break; case 'config': /** @var \Phacil\Framework\Config */ $config = self::getInstance(\Phacil\Framework\Config::class); return $config->get($argumentValue); break; default: return $argumentValue; break; } } /** * Sets an auto-instantiated object to be used as a dependency. * * This method allows you to set an object to be automatically instantiated and used as a dependency * when resolving other dependencies that require an instance of the specified class. * * @param object $class The object instance to be set as an auto-instantiated dependency. * @param string|null (Optional) $original The original class name of the object being set. * @return object The object instance that was set as an auto-instantiated dependency. * @throws \Phacil\Framework\Exception If the provided parameter is not an object. * @since 2.0.0 */ static public function setAutoInstance($class, $original = null) { if(!is_object($class)) throw new Exception('Object type is required!'); self::$autoInstances[$original ?: get_class($class)] = $class; return $class; } /** * * @param object $class * @return object * @since 2.0.0 * @throws \Phacil\Framework\Exception */ static public function getAutoInstance($class) { if (!is_object($class)) throw new Exception('Object type is required!'); return isset(self::$autoInstances[get_class($class)]) ? self::$autoInstances[get_class($class)] : self::setAutoInstance($class); } /** * Resolves and instantiates a class with optional constructor arguments and creation control. * * This method resolves a class by its name, instantiates it with optional constructor arguments, * and provides control over the creation process. * * @param string $class The fully qualified name of the class to instantiate. * @param array $args (Optional) Arguments for the class constructor. Default is an empty array. * If empty, constructor arguments will be automatically detected and injected. * @param bool $forceCreate (Optional) Whether to force creation. Default is false. * @return mixed * @throws \ReflectionException * @throws \Exception * @throws \Phacil\Framework\Exception */ public function injectionClass($class, $args = array(), $forceCreate = false) { $argsToInject = !empty($args) ? $args : false; $originalClass = $class; $class = self::checkPreference($class); $originalClass = $originalClass == $class ? null : $originalClass; if($class === get_class($this)) return $this; if (!$forceCreate && $autoInstance = $this->getInstance($originalClass ?: $class, [], true)) return $autoInstance; $refClass = new ReflectionClass($class); try { if (!$refClass->getConstructor() && !$argsToInject) { if ($refClass->hasMethod('getInstance') && $refClass->getMethod('getInstance')->isStatic()) { return self::setAutoInstance($refClass->getMethod('getInstance')->invoke(null), ($originalClass ?: $class)); } return self::setAutoInstance($refClass->newInstanceWithoutConstructor(), ($originalClass ?: $class)); } } catch (\Exception $th) { //throw $th; Don't make anything and retry create } try { $rMethod = new ReflectionMethod($class, "__construct"); if (!$argsToInject) { $params = $rMethod->getParameters(); $argsToInject = []; foreach ($params as $param) { //$param is an instance of ReflectionParameter try { //Check for the predefined type if(($typeFound = self::checkArgumentType($originalClass ?: $class, $param->getName())) !== null) { $argsToInject[$param->getPosition()] = $typeFound; continue; } if (version_compare(phpversion(), "7.2.0", "<")) { $declaringClass = $param->__toString(); $pattern = '/\[.*?>\s*([^$\s]+)/'; //$pattern = '/<[^>]+>\s*([^$\s]+)/'; if (preg_match($pattern, $declaringClass, $matches)) { $classFactoryName = $matches[1]; $classAttr = self::checkPreference($classFactoryName); if (class_exists($classAttr)) { $argsToInject[$param->getPosition()] = $this->injectionClass($classFactoryName); continue; } elseif (substr($classFactoryName, -(strlen(self::FACTORY_WORD_KEY))) === self::FACTORY_WORD_KEY && substr($classFactoryName, - (strlen(self::FACTORY_WORD_KEY)+1)) != "\\".self::FACTORY_WORD_KEY) { $factoredRefClass = substr($classFactoryName, 0, -(strlen(self::FACTORY_WORD_KEY))); if (!class_exists($classFactoryName) && $classFactoryName != self::FACTORY_CLASS) { $argsToInject[$param->getPosition()] = ($this->getInstance($classFactoryName, [], true)) ?: self::setAutoInstance($this->create(self::FACTORY_CLASS, [$factoredRefClass]), $classFactoryName); class_alias(self::FACTORY_CLASS, $classFactoryName); continue; } elseif ($classFactoryName != self::FACTORY_CLASS){ $argsToInject[$param->getPosition()] = ($this->getInstance($classFactoryName, [], true)) ?: self::setAutoInstance($this->create(self::FACTORY_CLASS, [$factoredRefClass]), $classFactoryName); continue; } } } if ($param->getClass()) { $injectionClass = $param->getClass()->name; $classAttr = self::checkPreference($injectionClass); if (class_exists($classAttr)) { $argsToInject[$param->getPosition()] = $this->injectionClass($injectionClass); continue; } if (!$param->isOptional()) { throw new \Phacil\Framework\Exception\ReflectionException("Error Processing Request: " . $injectionClass . " not exist"); } } } else { if ($param->getType()) { $injectionClass = $param->getType()->getName(); $classAttr = self::checkPreference($injectionClass); if (class_exists($classAttr)) { $argsToInject[$param->getPosition()] = $this->injectionClass($injectionClass); continue; } elseif (substr($injectionClass, -(strlen(self::FACTORY_WORD_KEY))) === self::FACTORY_WORD_KEY && substr($injectionClass, - (strlen(self::FACTORY_WORD_KEY) + 1)) != "\\" . self::FACTORY_WORD_KEY) { // Create a factored instance $factoredRefClass = substr($injectionClass, 0, -(strlen(self::FACTORY_WORD_KEY))); $argsToInject[$param->getPosition()] = ($this->getInstance($injectionClass, [], true)) ?: self::setAutoInstance($this->create(self::FACTORY_CLASS, [$factoredRefClass]), $injectionClass); class_alias(self::FACTORY_CLASS, $injectionClass); continue; } if (!$param->isOptional()) { throw new \Phacil\Framework\Exception\ReflectionException("Error Processing Request: " . $injectionClass . " not exist"); } } } } catch (\ReflectionException $th) { throw new \Phacil\Framework\Exception\ReflectionException($th->getMessage(), $th->getCode(), $th); } catch (\Exception $th) { throw new \Phacil\Framework\Exception($th->getMessage()); } if ($param->isOptional() && $param->isDefaultValueAvailable()) { $argsToInject[] = $param->getDefaultValue(); continue; } if ($param->isOptional()) { $argsToInject[] = null; } } } } catch (\ReflectionException $th) { throw new \Phacil\Framework\Exception\ReflectionException($th->getMessage()); } catch (\Exception $th) { throw new \Phacil\Framework\Exception($th->getMessage()); } return self::setAutoInstance($refClass->newInstanceArgs($argsToInject), $originalClass); } }