Overview

Namespaces

  • Alchemy
    • core
      • query
      • schema
    • dialect
    • engine
    • orm
    • tests
    • util
      • promise
  • PHP

Classes

  • Element

Interfaces

  • IElement
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: namespace Alchemy\core;
  4: 
  5: 
  6: /**
  7:  * Base class for composable elements
  8:  */
  9: class Element implements IElement {
 10: 
 11:     private static $id_counter = 0;
 12:     private static $typedefs = array();
 13: 
 14:     protected $id;
 15:     protected $type;
 16:     protected $tags = array();
 17: 
 18: 
 19:     /**
 20:      * Return a new instance of an Element type. If the type is an alias,
 21:      * may return an instance of a different subclass.
 22:      */
 23:     public static function __callStatic($name, $args) {
 24:         $def = self::get_definition($name);
 25: 
 26:         if ($args) {
 27:             array_unshift($args, $def['tags']['element.type']);
 28: 
 29:             $cls = new \ReflectionClass($def['tags']['element.class']);
 30:             return $cls->newInstanceArgs($args);
 31:         } else {
 32:             return new $def['tags']['element.class']($def['tags']['element.type']);
 33:         }
 34:     }
 35: 
 36: 
 37:     /**
 38:      * Define a new type for this Element class. Create an object of
 39:      * this type by calling Element::Type()
 40:      *
 41:      * @param  string $type new type identifier (defaults to class name)
 42:      * @param  string $base type to inherit from (defaults to class name)
 43:      * @param  array  $def  [description]
 44:      */
 45:     public static function define($type = null, $base = null, $def = array()) {
 46:         $parts = explode('\\', get_called_class());
 47:         $class = array_pop($parts);
 48: 
 49:         $type = $type ?: $class;
 50:         $base = $base ?: $class;
 51: 
 52:         // get base definition
 53:         $basedef = ($base != $type)
 54:             ? self::get_definition($base)
 55:             : array();
 56:         $basedef['tags']["element.type"] = $type;
 57:         $basedef['tags']["element.class"] = get_called_class();
 58: 
 59:         // merge with new definition (non-recursive)
 60:         foreach ($basedef as $k => $v) {
 61:             $def[$k] = array_key_exists($k, $def)
 62:                 ? (is_array($v) ? $def[$k] + $v : $def[$k])
 63:                 : $v;
 64:         }
 65: 
 66:         self::$typedefs["{$class}::{$type}"] = $def;
 67:     }
 68: 
 69: 
 70:     /**
 71:      * Define a new type as an alias to another type, possibly of a
 72:      * different class.
 73:      *
 74:      * @param  string $type new type identifier
 75:      * @param  string $base type to alias
 76:      */
 77:     public static function define_alias($type, $base) {
 78:         $parts = explode('\\', get_called_class());
 79:         $class = array_pop($parts);
 80: 
 81:         // copy type definition
 82:         self::$typedefs["{$class}::{$type}"] = self::get_definition($base);
 83:     }
 84: 
 85: 
 86:     /**
 87:      * Get the definition array for "Type" or "Class::Type"
 88:      *
 89:      * @param  string $type type identifier
 90:      * @return array        type definition
 91:      */
 92:     public static function get_definition($type) {
 93:         if (!strpos($type, '::')) {
 94:             $parts = explode('\\', get_called_class());
 95:             $class = array_pop($parts);
 96:             $type = "{$class}::{$type}";
 97:         }
 98: 
 99:         if (!array_key_exists($type, self::$typedefs)) {
100:             throw new \Exception("No Element definition for {$type}");
101:         }
102: 
103:         return self::$typedefs[$type];
104:     }
105: 
106: 
107:     /**
108:      * Convert an argument's structure to be similar to the default
109:      * ie. (5, array(array())) -> array(array(5))
110:      *
111:      * @return mixed
112:      */
113:     protected static function normalize_arg($arg, $default) {
114:         if (is_array($default)) {
115:             if (!is_array($arg)) {
116:                 $arg = !is_null($arg) ? array($arg) : $default;
117:             }
118: 
119:             foreach ($default as $k => $v) {
120:                 $arg[$k] = self::normalize_arg(array_key_exists($k, $arg) ? $arg[$k] : null, $v);
121:             }
122:         }
123: 
124:         return $arg ?: $default;
125:     }
126: 
127: 
128:     public function __clone() {
129:         $this->id = null;
130:     }
131: 
132: 
133:     public function __construct($type = null) {
134:         if ($type) {
135:             $def = self::get_definition($type);
136:             $this->addTags($def['tags']);
137:         }
138:         $this->type = $type;
139:     }
140: 
141: 
142:     /**
143:      * Apply a tag to this element. The same tag cannot be applied
144:      * with two different values.
145:      *
146:      * @param string $tag
147:      * @param string $value optional value to give tag
148:      */
149:     public function addTag($tag, $value = true) {
150:         if (array_key_exists($tag, $this->tags) && $this->tags[$tag] !== true) {
151:             if ($value === true) {
152:                 return;
153:             } elseif ($value !== $this->tags[$tag]) {
154:                 throw new \Exception("Tag '{$tag}' already has value {$this->tags[$tag]}, cannot reapply with value '{$value}'");
155:             }
156:         }
157: 
158:         $this->tags[$tag] = $value;
159:     }
160: 
161: 
162:     public function addTags($tags) {
163:         foreach ($tags as $tag => $value) {
164:             $this->addTag($tag, $value);
165:         }
166:     }
167: 
168: 
169:     public function copy() {
170:         return clone $this;
171:     }
172: 
173: 
174:     public function getDescription($maxdepth = 3, $curdepth = 0) {
175:         $parts = explode('\\', get_called_class());
176:         $class = array_pop($parts);
177:         return "{$class}::{$this->type} #{$this->getID()}";
178:     }
179: 
180: 
181:     /**
182:      * Get the locally-unique element id
183:      *
184:      * @return string
185:      */
186:     public function getID() {
187:         if (!$this->id) {
188:             $this->id = ++self::$id_counter;
189:         }
190: 
191:         return $this->id;
192:     }
193: 
194: 
195:     /**
196:      * If the tag has been applied to this object, returns its
197:      * value, else false
198:      *
199:      * @param  string $tag tag name
200:      * @return mixed       value or false
201:      */
202:     public function getTag($tag) {
203:         if ($tag && array_key_exists($tag, $this->tags)) {
204:             return $this->tags[$tag];
205:         }
206: 
207:         return false;
208:     }
209: 
210: 
211:     public function getType() {
212:         return $this->type;
213:     }
214: 
215: 
216:     /**
217:      * List of all tags that apply to this element
218:      *
219:      * @return array
220:      */
221:     public function listTags() {
222:         return array_keys(array_filter($this->tags));
223:     }
224: }
225: 
API documentation generated by ApiGen 2.8.0