1: <?php
2:
3: namespace Alchemy\core;
4:
5:
6: 7: 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: 21: 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: 39: 40: 41: 42: 43: 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:
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:
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: 72: 73: 74: 75: 76:
77: public static function define_alias($type, $base) {
78: $parts = explode('\\', get_called_class());
79: $class = array_pop($parts);
80:
81:
82: self::$typedefs["{$class}::{$type}"] = self::get_definition($base);
83: }
84:
85:
86: 87: 88: 89: 90: 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: 109: 110: 111: 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: 144: 145: 146: 147: 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: 183: 184: 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: 197: 198: 199: 200: 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: 218: 219: 220:
221: public function listTags() {
222: return array_keys(array_filter($this->tags));
223: }
224: }
225: