/** * @param $class Reflection_Class * @param $composite_class_name string * @return Reflection_Property_Value[] */ protected function getProperties(Reflection_Class $class, $composite_class_name = null) { $properties = []; if (isset($composite_class_name) && isA($class->name, Component::class)) { $composite_property = call_user_func([$class->name, 'getCompositeProperties'], $composite_class_name); $composite_property = reset($composite_property); } else { $composite_property = null; } if ($class->getAnnotation('link')->value) { $link_class = new Link_Class($class->name); $composite_link_property = $link_class->getCompositeProperty(); foreach ($link_class->getProperties([T_EXTENDS, T_USE]) as $property) { if ((!$composite_property || $property->name !== $composite_property->name) && (!$composite_link_property || $property->name !== $composite_link_property->name) && !$property->isStatic() && !$property->getListAnnotation('user')->has(User_Annotation::INVISIBLE)) { $properties[] = $property; } } } else { foreach ($class->getProperties([T_EXTENDS, T_USE]) as $property) { if ((empty($composite_property) || $property->name !== $composite_property->name) && !$property->isStatic() && !$property->getListAnnotation('user')->has(User_Annotation::INVISIBLE)) { $properties[] = $property; } } } return $properties; }
/** * Default remover removes an object from all collections properties of the object * * @param $composite object The object that contains the given object * @param $object object contained object to remove * @return integer removed instances count */ public static function removeObjectFromComposite($composite, $object) { $count = 0; foreach ((new Reflection_Class(get_class($composite)))->accessProperties() as $property) { $type = $property->getType(); if ($type->isClass() && isA($object, $type->getElementTypeAsString())) { $property_name = $property->name; if ($type->isMultiple()) { $remover = $property->getAnnotation('remover'); if ($remover->value) { $count += call_user_func([$composite, $remover->value], $object); } else { foreach ($composite->{$property_name} as $key => $value) { if ($value === $object) { unset($composite->{$property_name}[$key]); $count++; } } } } elseif ($property->getValue($composite) === $object) { unset($composite->{$property_name}); $count++; } } } return $count; }
/** * Get top object that is an instance of $class_name from the call stack * * @param $class_name string Can be a the name of a class, interface or trait * @return object|null */ public function getObject($class_name) { foreach ($this->stack as $stack) { if (isset($stack['object']) && isA($stack['object'], $class_name)) { return $stack['object']; } } return null; }
/** * @before SAF\Framework\Dao\Data_Link::write($this) */ public function setNumber() { if (isA($this, Has_Counter::class)) { /** @var $counter Counter */ $counter = Dao::searchOne(['class_name' => get_class($this)], Counter::class); if (!isset($counter)) { $counter = Builder::create(Counter::class, [get_class($this)]); } $this->setCounter($counter->increment()); } }
/** * Guess value for @link using the type of the property (@var) * - property is a Date_Time (or a child class) : @link will be 'DateTime' * - property is a single object : @link will be 'Object' * - property is a collection object with '@var Object[] All' : @link will be 'All' * - property is a collection object with '@var Object[] Collection' : @link will be 'Collection' * - property is a collection object with '@var Object[] Map' : @link will be 'Map' * - property is a collection object without telling anything : * - @link will be 'Collection' if the object is a Component * - @link will be 'Map' if the object is not a Component * * @param $property Reflection_Property * @return string returned guessed value for @link */ private function guessValue(Reflection_Property $property) { if ($property->getType()->isMultiple()) { /** @var $var_annotation Var_Annotation */ $value = lParse($property->getAnnotation('var'), SP); if (empty($value)) { $value = isA($property->getType()->getElementTypeAsString(), Component::class) ? self::COLLECTION : self::MAP; } } else { $value = isA($property->getType()->asString(), Date_Time::class) ? self::DATETIME : self::OBJECT; } return $value; }
/** * Default disposer call the remove * * @param $class_name string|object The composite class name or object * @param $property_name string The composite property name */ public function dispose($class_name = null, $property_name = null) { foreach (self::getCompositeProperties($class_name, $property_name) as $property) { $composite = $property->getValue($this); if (isset($composite)) { if (isA($composite, Remover::class)) { /** @var $composite Remover */ $composite->remove($this); } else { Remover_Tool::removeObjectFromComposite($composite, $this); } } } }
/** * Generic getter for a collection of objects * * @param $stored Component[] Actual value of the property (will be returned if not null) * @param $class_name string Class for each collection's object * @param $object object Parent object * @param $property string|Reflection_Property Parent property (or property name). Recommended * but can be omitted if foreign class is a Component * @return object[] */ public static function &getCollection(&$stored, $class_name, $object, $property = null) { if (!(self::$ignore || isset($stored))) { if (Dao::getObjectIdentifier($object)) { $search_element = Search_Object::create($class_name); $is_component = isA($search_element, Component::class); if (isset($property)) { if (!$property instanceof Reflection_Property) { $property = new Reflection_Property(get_class($object), $property); } $property_name = $property->getAnnotation('foreign')->value; $dao = ($dao = $property->getAnnotation('dao')->value) ? Dao::get($dao) : Dao::current(); } else { $dao = Dao::current(); $property_name = null; } if ($is_component) { /** @var $search_element Component */ $search_element->setComposite($object, $property_name); $link_properties_names = (new Link_Class($class_name))->getLinkPropertiesNames(); $options = $link_properties_names ? [Dao::sort(), Dao::key($link_properties_names)] : [Dao::sort()]; $stored = $dao->search($search_element, null, $options); } elseif (!empty($property_name)) { $property = new Reflection_Property(get_class($search_element), $property_name); $accessible = $property->isPublic(); if (!$accessible) { $property->setAccessible(true); } $property->setValue($search_element, $object); if (!$accessible) { $property->setAccessible(false); } $stored = $dao->search($search_element, null, [Dao::sort()]); } else { trigger_error('getCollection() must be called for a component foreign type' . ' or with a parent property name', E_USER_ERROR); } } if (!isset($stored)) { $stored = []; } } return $stored; }
/** * @param $view string * @param $view_method_name string * @param $parameters array * @param $form array * @param $files array * @param $class_name string * @param $feature_name string * @return mixed */ private static function executeView($view, $view_method_name, $parameters, $form, $files, $class_name, $feature_name) { $object = reset($parameters); $view_object = is_object($object) && isA($object, $view) ? reset($parameters) : Builder::create($view); return $view_object->{$view_method_name}($parameters, $form, $files, $class_name, $feature_name); }
/** * Returns true if the class type uses the given trait * * This goes into parents traits * * @param $trait_name string * @return boolean */ public function usesTrait($trait_name) { return $this->isClass() && isA($this->getElementTypeAsString(), $trait_name); }
/** * Parse a variable / function / include and returns its return value * * @param $var_name string can be an unique var or path.of.vars * @param $as_string boolean if true, returned value will always be a string * @return string|object var value after reading value / executing specs */ protected function parseValue($var_name, $as_string = true) { if ($var_name === DOT) { return reset($this->objects); } elseif ($var_name == '') { return ''; } elseif ($var_name[0] === SL) { return $this->parseInclude($var_name); } elseif ($var_name[0] == '!') { $not = true; $var_name = substr($var_name, 1); } if (substr($var_name, -1) == '*') { $group = true; $var_name = substr($var_name, 0, -1); } if (strpos('-+', $var_name[0]) !== false) { $descendants_names = $this->descendants_names; $descendants = $this->descendants; $var_names = $this->var_names; $objects = $this->objects; while ($var_name[0] === '-') { array_unshift($this->descendants_names, array_shift($this->var_names)); array_unshift($this->descendants, array_shift($this->objects)); $var_name = substr($var_name, 1); } while ($var_name[0] === '+') { array_unshift($this->var_names, array_shift($this->descendants_names)); array_unshift($this->objects, array_shift($this->descendants)); $var_name = substr($var_name, 1); } } $property_name = null; if ($var_name === DOT) { $object = reset($this->objects); } elseif (strpos($var_name, DOT) !== false) { $object = null; if (!isset($var_names)) { $var_names = $this->var_names; } if (!isset($objects)) { $objects = $this->objects; } $parenthesis = ''; foreach (explode(DOT, $var_name) as $property_name) { if ($parenthesis) { $property_name = $parenthesis . DOT . $property_name; $parenthesis = ''; } if (strpos($property_name, '(') && substr_count($property_name, '(') > substr_count($property_name, ')')) { $parenthesis = $property_name; } else { $object = $this->parseSingleValue($property_name); if (strlen($property_name)) { array_unshift($this->var_names, $property_name); array_unshift($this->objects, $object); } } } } else { $object = $this->parseSingleValue($var_name); } // if the parse value finishes with a class name : check if the last object is any of this class if (isset($this->parse_class_name)) { $object = isA(reset($this->objects), $this->parse_class_name); unset($this->parse_class_name); } // parse object to string if ($as_string && is_object($object)) { if ($object instanceof File) { $object = $this->parseFileToString(null, $object); } else { $object = $this->parseObjectToString($object, $property_name); } } // parse not if (isset($not)) { $object = !$object; } // restore position arrays if (isset($objects)) { $this->objects = $objects; } if (isset($var_names)) { $this->var_names = $var_names; } if (isset($descendants)) { $this->descendants = $descendants; } if (isset($descendants_names)) { $this->descendants_names = $descendants_names; } return isset($group) ? $this->group($var_name, $object) : $object; }
/** * @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 $class_name string * @param $search string * @param $exclude_properties string[] * @param $prefix string * @param $depth integer * @return Reflection_Property[] */ protected function searchProperties($class_name, $search, $exclude_properties = [], $prefix = '', $depth = 0) { $class = new Reflection_Class($class_name); $all_properties = $this->getProperties($class); $first_properties = []; $properties = []; $more_properties = []; foreach ($all_properties as $property) { if (!isset($exclude_properties[$property->name])) { $property_path = $prefix . $property->name; if ($property->name == $search || $property_path == $search) { $first_properties[$property_path] = $property; $matches = true; } else { preg_match('|^' . $search . '|', strtolower(strSimplify(Loc::tr($property->name), SP)), $matches); preg_match('|^' . $search . '|', strtolower(strSimplify(Loc::tr($prefix . $property_path), DOT . SP)), $matches2); if ($matches || $matches2) { $properties[$property_path] = $property; $matches = true; } else { preg_match('|' . $search . '|', strtolower(strSimplify(Loc::tr($property_path), DOT . SP)), $matches); if ($matches) { $more_properties[$property_path] = $property; } } } if ($depth < $this->max_depth && !$matches) { $type = $property->getType(); if ($type->isClass()) { $property_class = $type->getElementTypeAsString(); $is_component = isA($property_class, Component::class); $exclude_sub_properties = $is_component ? call_user_func([$property_class, 'getCompositeProperties'], $class_name) : []; $parent_classes[] = $class_name; $sub_properties = $this->searchProperties($type->getElementTypeAsString(), $search, $exclude_sub_properties, $property->name . DOT, $depth + 1); foreach ($sub_properties as $sub_property) { if (!isset($exclude_properties[$sub_property->name])) { $more_properties[] = new Reflection_Property($class_name, $property->name . DOT . $sub_property->path); } } } } } } $properties = array_merge($first_properties, $properties, $more_properties); return $properties; }
/** * @param $elements object[]|Component[] the component objects * @param $composite_class_name string the composite class name */ private function removeCompositeFromComponents($elements, $composite_class_name) { if (isA($element = reset($elements), Component::class)) { $getCompositeProperty = [get_class($element), 'getCompositeProperty']; if ($composite_property = call_user_func($getCompositeProperty, $composite_class_name)) { foreach ($elements as $element) { $property_name = $composite_property->name; $id_property_name = 'id_' . $property_name; if (isset($element->{$property_name})) { $this->dao->disconnect($element->{$property_name}); } unset($element->{$id_property_name}); } } } }
/** * Returns true if the class has $name into its parents, interfaces or traits * * @param $name string * @param $flags integer[] T_EXTENDS, T_IMPLEMENTS, T_USE * @return boolean */ public function isA($name, $flags = []) { if ($flags) { $flip = array_flip($flags); if (isset($flip[T_USE]) && trait_exists($name)) { return isA($this->name, $name); } elseif (isset($flip[T_EXTENDS]) && class_exists($name) || isset($flip[T_IMPLEMENTS]) && interface_exists($name)) { return is_a($this->name, $name, true); } } return false; }