/** * Returns true if an object / class /interface / trait is a class / interface / trait * * All parent classes, interfaces and traits are scanned recursively * * @param $object string|object * @param $class_name string|object * @return boolean */ function isA($object, $class_name) { if (is_string($object)) { $object = Builder::className($object); } elseif (is_object($object)) { $object = get_class($object); } else { return false; } if (is_object($class_name)) { $class_name = get_class($class_name); } if (is_a($object, $class_name, true)) { return true; } $classes = class_parents($object) + class_uses($object); while ($classes) { $next_classes = []; foreach ($classes as $class) { if (is_a($class, $class_name, true)) { return true; } $next_classes += class_uses($class); } $classes = $next_classes; } return false; }
/** * @param $elements Setting[] */ public function __construct($elements = null) { $settings = []; if (isset($elements)) { foreach ($elements as $setting) { $settings[$setting->code] = $setting; } } parent::__construct(Builder::className('SAF\\Framework\\Setting'), $settings); }
/** * Apply class name : if constructor was called without columns, this will initialize columns list * * This applies default column names if there was no default class name, or if class name changed, * or if there were no column names. * * @param $class_name string */ private function applyClassName($class_name) { if (isset($class_name) && $class_name != $this->class_name && (isset($this->class_name) || !isset($this->columns))) { $class_name = Builder::className($class_name); $this->class_name = $class_name; $columns = (new Reflection_Class($class_name))->getListAnnotation('sort')->values(); if (!$columns) { $columns = (new Reflection_Class($class_name))->getListAnnotation('representative')->values(); } $this->columns = $columns; $this->calculateReverse(); } }
/** * Apply namespace and use entries to the type name (if class) * * Return the full element class name, used to modify the type (multiple stays multiple) * * @param $namespace string * @param $use string[] * @return string */ public function applyNamespace($namespace, $use = []) { /** @var $this Annotation|Types_Annotation */ /** @var $values string[] */ $values = is_array($this->value) ? $this->value : [$this->value]; foreach ($values as $key => $class_name) { if (ctype_upper($class_name[0])) { if (substr($class_name, -2) == '[]') { $class_name = substr($class_name, 0, -2); $multiple = '[]'; } else { $multiple = ''; } $values[$key] = Builder::className((new Type($class_name))->applyNamespace($namespace, $use)) . $multiple; } elseif ($class_name[0] === BS) { $values[$key] = substr($class_name, 1); } } $this->value = is_array($this->value) ? $values : reset($values); }
/** * @param $source Reflection_Source the PHP source file object * @param $compiler PHP\Compiler the main compiler * @return boolean true if compilation process did something, else false */ public function compile(Reflection_Source $source, PHP\Compiler $compiler = null) { $compiled = false; foreach ($source->getClasses() as $class) { // replace extends with the built replacement class if (!Builder::isBuilt($class->name)) { $parent_class_name = $class->getParentName(); if ($parent_class_name) { $replacement_class_name = Builder::className($parent_class_name); if ($replacement_class_name != $parent_class_name) { $class_exists = class_exists($replacement_class_name, false); if ($class_exists && is_a($replacement_class_name, $parent_class_name) || !$class_exists && Reflection_Source::of($replacement_class_name)->getClass($replacement_class_name)->isA($class->name)) { $replacement_class_name = $parent_class_name; } } if (is_array($replacement_class_name)) { trigger_error("Replacement classes should all be compiled", E_USER_ERROR); $compiler->addSource($source); } elseif ($replacement_class_name !== $parent_class_name && Builder::isBuilt($replacement_class_name)) { $extended = null; $buffer = $source->getSource(); $short_class_name = Namespaces::shortClassName($class->name); $buffer = preg_replace_callback('%(\\s+class\\s+' . $short_class_name . '\\s+extends\\s+)([\\\\\\w]+)(\\s+)%', function ($match) use(&$extended, $replacement_class_name) { $extended = $match[2]; return $match[1] . BS . $replacement_class_name . $match[3]; }, $buffer); if ($extended) { $buffer = preg_replace_callback('%(\\n\\s+\\*\\s+@link\\s+)(' . str_replace(BS, BS . BS, $extended) . ')(\\s+)%', function ($match) use($replacement_class_name) { return $match[1] . BS . $replacement_class_name . $match[3]; }, $buffer); } $source->setSource($buffer); $compiled = true; } } } } return $compiled; }
/** * Add a link class (using the 'link' class annotation) to joins * * @param $path string the property path * @param $class Link_Class the link class itself (which contains the @link) * @param $linked_class_name string the linked class name (the value of @link) * @param $join_mode string * @return Reflection_Property[] the properties that come from the linked class, * for further exclusion */ private function addLinkedClass($path, $class, $linked_class_name, $join_mode) { $linked_class = new Reflection_Class($linked_class_name); $join = new Join(); $join->master_alias = 't' . ($this->alias_counter - 1); $join->master_column = 'id_' . $class->getCompositeProperty($linked_class_name)->getAnnotation('storage')->value; $join->foreign_alias = 't' . $this->alias_counter++; $join->foreign_column = 'id'; $join->foreign_class = Builder::className($linked_class_name); $join->foreign_table = Dao::storeNameOf($join->foreign_class); $join->mode = $join_mode == Join::LEFT ? Join::LEFT : Join::INNER; $join->type = Join::LINK; if (!isset($this->joins[$path])) { // this ensures that the main path is set before the linked path $this->joins[$path] = null; } $this->joins[($path ? $path . '-' : '') . $join->foreign_table . '-@link'] = $join; $this->id_link_joins[$path] = $join; $this->link_joins[$path] = $join; $more_linked_class_name = $linked_class->getAnnotation('link')->value; $exclude_properties = $more_linked_class_name ? $this->addLinkedClass($path, $class, $more_linked_class_name, $join_mode) : []; foreach ($linked_class->getProperties([T_EXTENDS, T_USE]) as $property) { if (!$property->isStatic()) { if (!isset($exclude_properties[$property->name])) { $this->properties[$linked_class_name][$property->name] = $property; $property_path = ($path ? $path . DOT : '') . $property->name; $type = $property->getType(); if ($type->isClass()) { $this->classes[$property_path] = $property->getType()->getElementTypeAsString(); } $this->link_joins[$property_path] = $join; $exclude_properties[$property->name] = true; } } } return $exclude_properties; }
/** * @param $doc_comment string * @param $annotation_name string * @param $i integer * @param $annotation_class string * @param $reflection_object Has_Doc_Comment|Reflection * @return Annotation */ private static function parseAnnotationValue($doc_comment, $annotation_name, &$i, $annotation_class, Reflection $reflection_object) { $i += strlen($annotation_name) + 1; $next_char = $doc_comment[$i]; switch ($next_char) { case SP: case TAB: $i++; $j = strlen($doc_comment); $next_annotation = strpos($doc_comment, SP . '* @', $i); $end_doc_comment = strpos($doc_comment, SP . '*/', $i); $next_in = strpos($doc_comment, LF . self::DOC_COMMENT_IN, $i); if ($next_annotation !== false && $next_annotation < $j) { $j = $next_annotation; } if ($end_doc_comment !== false && $end_doc_comment < $j) { $j = $end_doc_comment; } if ($next_in !== false && $next_in < $j) { $j = $next_in; } if ($j === false) { trigger_error('Missing doc_comment end', E_USER_ERROR); } $value = trim(preg_replace('%\\s*\\n\\s+\\*\\s*%', '', substr($doc_comment, $i, $j - $i))); break; case CR: case LF: $value = true; break; default: $value = null; } /** @var $annotation Annotation */ $annotation = isset($value) ? new $annotation_class($value, $reflection_object, $annotation_name) : null; if (isset($annotation) && isA($annotation, Annotation_In::class)) { /** @var $annotation Annotation_In */ $j = strrpos(substr($doc_comment, 0, $i), LF . self::DOC_COMMENT_IN); if ($j === false) { $annotation->class_name = $reflection_object instanceof Reflection_Class_Component ? $reflection_object->getDeclaringClassName() : $reflection_object->getName(); } else { $j += strlen(self::DOC_COMMENT_IN) + 1; $k = strpos($doc_comment, LF, $j); $annotation->class_name = substr($doc_comment, $j, $k - $j); } } if (isset($annotation) && isA($annotation, Types_Annotation::class)) { $do = false; if (is_array($annotation->value)) { foreach ($annotation->value as $value) { if ($value && (ctype_upper($value[0]) || $value[0] == BS)) { $do = true; break; } } } else { $do = $annotation->value && ctype_upper($annotation->value[0]); } if ($do) { /** @var $annotation Types_Annotation */ $j = strrpos(substr($doc_comment, 0, $i), LF . self::DOC_COMMENT_IN); if ($j === false) { $class_name = $reflection_object instanceof Reflection_Class_Component ? $reflection_object->getDeclaringClassName() : $reflection_object->getName(); $namespace = Namespaces::of($class_name); $use = PHP\Reflection_Class::of($class_name)->getNamespaceUse(); } else { $j += strlen(self::DOC_COMMENT_IN) + 1; $k = strpos($doc_comment, LF, $j); $in_class = substr($doc_comment, $j, $k - $j); $namespace = Namespaces::of($in_class); $use = PHP\Reflection_Class::of($in_class)->getNamespaceUse(); } $annotation->applyNamespace($namespace, $use); } elseif (is_array($annotation->value)) { foreach ($annotation->value as $key => $value) { $annotation->value[$key] = Builder::className($value); } } else { if ($annotation->value[0] === BS) { $annotation->value = substr($annotation->value, 1); } $annotation->value = Builder::className($annotation->value); } } return $annotation; }
/** * @param $property Reflection_Property * @return Reflection_Class */ private function getForeignClass(Reflection_Property $property) { $type = $property->getType(); $foreign_class_name = Builder::className($type->getElementTypeAsString()); if ($property instanceof PHP\Reflection_Property) { $foreign_class = PHP\Reflection_Class::of($foreign_class_name); } else { $reflection_class = new Reflection\Reflection_Class(get_class($property->getDeclaringClass())); $foreign_class = $reflection_class->newInstance($foreign_class_name); } return $foreign_class; }
/** * @param $value string class name taken from the import array * @return string class name (built and with added namespace, if needed) */ public static function getClassNameFromValue($value) { return isset($value) ? Builder::className($value) : null; }
/** * Gets URI parameter as an object * * Object is of class $parameter name, and is read from current data link using the parameter * value as identifier. * * @param $parameter_name string * @return object */ public function getObject($parameter_name) { if (isset($this->objects[$parameter_name])) { // parameter is in cache $object = $this->objects[$parameter_name]; } elseif (is_numeric($this->getRawParameter($parameter_name))) { if (isset($parameter_name[0]) && ctype_upper($parameter_name[0])) { $class_name = $parameter_name; } if (isset($class_name) && @class_exists($class_name)) { // object parameter $object = $this->getRawParameter($parameter_name) + 0; Mapper\Getter::getObject($object, $class_name); $this->objects[$parameter_name] = $object; } else { // free parameter $object = $this->getRawParameter($parameter_name); $this->objects[$parameter_name] = $object; } } else { // text parameter $object = $this->getRawParameter($parameter_name); if (is_string($object) && $object && $object[0] === SL) { $object = new Uri($object); } $this->objects[$parameter_name] = $object; } if (empty($object)) { $built_parameter_name = Builder::className($parameter_name); if ($built_parameter_name != $parameter_name) { return $this->getObject(Builder::className($parameter_name)); } } return $object; }
/** * Search objects from data source * * It is highly recommended to instantiate the $what object using Search_Object::instantiate() in order to initialize all properties as unset and build a correct search object. * If some properties are an not-loaded objects, the search will be done on the object identifier, without joins to the linked object. * If some properties are loaded objects : if the object comes from a read, the search will be done on the object identifier, without join. If object is not linked to data-link, the search is done with the linked object as others search criterion. * * @param $what object|array source object for filter, or filter array (need class_name) only set properties will be used for search * @param $class_name string must be set if $what is a filter array and not an object * @param $options Option[] some options for advanced search * @return object[] a collection of read objects */ public function search($what, $class_name = null, $options = []) { if (!isset($class_name)) { $class_name = get_class($what); } $class_name = Builder::className($class_name); $builder = new Select($class_name, null, $what, $this, $options); $query = $builder->buildQuery(); $this->setContext($builder->getJoins()->getClassNames()); $result_set = $this->connection->query($query); if ($options) { $this->getRowsCount($result_set, 'SELECT', $options); } $objects = $this->fetchAll($class_name, $options, $result_set); $this->afterReadMultiple($objects, $options); return $objects; }
/** * @param $class_name string */ public function setClass($class_name) { if ($this->started) { $this->stop(); } $this->class = new Reflection_Class(Builder::className($class_name)); $this->defaults = $this->class->getDefaultProperties(); }
/** * Generate a new Sql_Join instance, with initialization of all it's properties * * @param $mode integer * @param $master_alias string * @param $master_column string * @param $foreign_table string * @param $foreign_alias string * @param $foreign_column string * @param $type string * @param $foreign_class string * @return Join */ public static function newInstance($mode, $master_alias, $master_column, $foreign_alias, $foreign_table, $foreign_column, $type = self::SIMPLE, $foreign_class = null) { $sql_join = new Join(); $sql_join->foreign_alias = $foreign_alias; $sql_join->foreign_class = isset($foreign_class) ? Framework\Builder::className($foreign_class) : null; $sql_join->foreign_column = $foreign_column; $sql_join->foreign_table = $foreign_table; $sql_join->master_alias = $master_alias; $sql_join->master_column = $master_column; $sql_join->mode = $mode; $sql_join->type = $type; return $sql_join; }
/** * @param $base_class string The base name for the class, ie 'SAF\Framework\User' * @param $feature_name string The name of the feature, ie 'dataList' * @param $suffix string Class suffix, ie 'Controller', 'View' * @param $extension string File extension, ie 'php', 'html' * @param $class_form boolean true to use 'Feature_Class' naming instead of 'featureClass' * @return string[] [$class, $method] */ public static function get($base_class, $feature_name, $suffix, $extension, $class_form = true) { // ie : $feature_class = 'featureName' transformed into 'Feature_Name' $feature_class = Names::methodToClass($feature_name); // $feature_what is $feature_class or $feature_name depending on $class_name $feature_what = $class_form ? $feature_class : $feature_name; $_suffix = $suffix ? '_' . $suffix : ''; $class_name = $base_class; $ext = DOT . $extension; $method = 'run'; $application_classes = Application::current()->getClassesTree(); // $classes : the controller class name and its parents // ['Vendor\Application\Module\Class_Name' => '\Module\Class_Name'] $classes = []; do { $classes[$class_name] = substr($class_name, strpos($class_name, BS, strpos($class_name, BS) + 1) + 1); if (@class_exists($class_name)) { $reflection_class = new Reflection_Class(Builder::className($class_name)); foreach ($reflection_class->getTraits() as $trait) { $classes[$trait->name] = explode(BS, $trait->name, 3)[2]; } foreach ($reflection_class->getListAnnotation('extends')->values() as $extends) { $classes[$extends] = explode(BS, $extends, 3)[2]; } } $class_name = @get_parent_class($class_name); } while ($class_name); // Looking for specific controller for each application $application_class = reset($application_classes); do { $namespace = Namespaces::of($application_class); // for the controller class and its parents foreach ($classes as $short_class_name) { $class_name = $namespace . BS . $short_class_name; $path = strtolower(str_replace(BS, SL, $class_name)); if (isset($GLOBALS['D'])) { echo '- try A1 ' . $path . SL . $feature_what . $_suffix . $ext . BR; } if (file_exists($path . SL . $feature_what . $_suffix . $ext)) { $class = $class_name . BS . $feature_what . $_suffix; break 2; } if (isset($GLOBALS['D'])) { echo '- try A2 ' . $path . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext . BR; } if (file_exists($path . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext)) { $class = $class_name . BS . $feature_class . BS . $feature_what . $_suffix; break 2; } if (isset($GLOBALS['D']) && $suffix) { echo '- try A3 ' . $path . SL . strtolower($feature_class) . SL . $suffix . $ext . BR; } if ($suffix && file_exists($path . SL . strtolower($feature_class) . SL . $suffix . $ext)) { $class = $class_name . BS . $feature_class . BS . $suffix; break 2; } if (isset($GLOBALS['D'])) { echo '- try A4 ' . Names::classToPath($class_name) . '_' . $feature_what . $_suffix . $ext . BR; } if (file_exists(Names::classToPath($class_name) . '_' . $feature_what . $_suffix . $ext)) { $class = $class_name . '_' . $feature_what . $_suffix; break 2; } if (isset($GLOBALS['D']) && $suffix) { echo '- try A5 ' . $path . SL . $suffix . $ext . BR; } if ($suffix && file_exists($path . SL . $suffix . $ext) && method_exists($class_name . BS . $suffix, 'run' . ucfirst($feature_name))) { $class = $class_name . BS . $suffix; $method = 'run' . ucfirst($feature_name); break 2; } } // next application is the parent one $application_class = next($application_classes); } while ($application_class); // Looking for default controller for each application if (empty($class)) { reset($application_classes); do { // looking for default controller $path = strtolower(str_replace(BS, SL, $namespace)); if (isset($GLOBALS['D']) && $suffix) { echo '- try B1 ' . $path . SL . strtolower($feature_class) . SL . $suffix . $ext . BR; } if ($suffix && file_exists($path . SL . strtolower($feature_class) . SL . $suffix . $ext)) { $class = $namespace . BS . $feature_class . BS . $suffix; break; } if (isset($GLOBALS['D'])) { echo '- try B2 ' . $path . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext . BR; } if (file_exists($path . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext)) { $class = $namespace . BS . $feature_class . BS . $feature_what . $_suffix; break; } if (isset($GLOBALS['D']) && $suffix) { echo '- try B3 ' . $path . SL . 'widget' . SL . strtolower($feature_class) . SL . $suffix . $ext . BR; } if ($suffix && file_exists($path . SL . 'widget' . SL . strtolower($feature_class) . SL . $suffix . $ext)) { $class = $namespace . BS . 'Widget' . BS . $feature_class . BS . $suffix; break; } if (isset($GLOBALS['D'])) { echo '- try B4 ' . $path . SL . 'widget' . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext . BR; } if (file_exists($path . SL . 'widget' . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext)) { $class = $namespace . BS . 'Widget' . BS . $feature_class . BS . $feature_what . $_suffix; break; } if (isset($GLOBALS['D']) && $suffix) { echo '- try B5 ' . $path . SL . 'webservice' . SL . strtolower($feature_class) . SL . $suffix . $ext . BR; } if ($suffix && file_exists($path . SL . 'webservice' . SL . strtolower($feature_class) . SL . $suffix . $ext)) { $class = $namespace . BS . 'Webservice' . BS . $feature_class . BS . $suffix; break; } if (isset($GLOBALS['D'])) { echo '- try B6 ' . $path . SL . 'webservice' . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext . BR; } if (file_exists($path . SL . 'webservice' . SL . strtolower($feature_class) . SL . $feature_what . $_suffix . $ext)) { $class = $namespace . BS . 'Webservice' . BS . $feature_class . BS . $feature_what . $_suffix; break; } // next application is the parent one } while (next($application_classes)); // Looking for direct feature call, without using any controller static $last_controller_class = ''; static $last_controller_method = ''; if (empty($class) && (strpos($suffix, 'View') === false || $last_controller_class !== $base_class && $last_controller_method !== $feature_name)) { if (strpos($suffix, 'Controller') !== false) { $last_controller_class = $base_class; $last_controller_method = $feature_name; } if (@method_exists($base_class, $feature_name)) { $class = $base_class; $method = $feature_name; } } // Looking for default controller for each application if (empty($class) && $suffix) { reset($application_classes); // $suffix == 'Html_View' => $sub = 'View/Html', $suffix = 'View' if (strpos($suffix, '_')) { $elements = explode('_', $suffix); $sub = join(SL, array_reverse($elements)); $suffix = end($elements); } else { $sub = $suffix; } do { if (isset($GLOBALS['D'])) { echo '- try C2 ' . $path . SL . strtolower($sub) . '/Default_' . $suffix . $ext . BR; } if (file_exists($path . SL . strtolower($sub) . '/Default_' . $suffix . $ext)) { $class = $namespace . BS . str_replace(SL, BS, $sub) . BS . 'Default_' . $suffix; break; } } while (next($application_classes)); } } $result = [isset($class) ? $class : null, $method]; if (isset($GLOBALS['D'])) { echo '- FOUND ' . join('::', $result) . BR; } return $result; }