Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • CallbackFilterIterator
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • LimitedScope
  • MimeTypeDetector
  • ObjectMixin
  • Paginator
  • Random
  • RecursiveCallbackFilterIterator
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

  • AssertionException
  • JsonException
  • RegexpException
  • TokenizerException
  • UnknownImageFileException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Utils;
  9: 
 10: use Nette,
 11:     Nette\MemberAccessException;
 12: 
 13: 
 14: /**
 15:  * Nette\Object behaviour mixin.
 16:  *
 17:  * @author     David Grudl
 18:  */
 19: class ObjectMixin
 20: {
 21:     /** @var array (name => 0 | bool | array)  used by getMethods() */
 22:     private static $methods;
 23: 
 24:     /** @var array (name => 'event' | TRUE)  used by hasProperty() */
 25:     private static $props;
 26: 
 27:     /** @var array (name => array(type => callback))  used by get|setExtensionMethod() */
 28:     private static $extMethods;
 29: 
 30: 
 31:     /**
 32:      * Static class - cannot be instantiated.
 33:      */
 34:     final public function __construct()
 35:     {
 36:         throw new Nette\StaticClassException;
 37:     }
 38: 
 39: 
 40:     /**
 41:      * __call() implementation.
 42:      * @param  object
 43:      * @param  string
 44:      * @param  array
 45:      * @return mixed
 46:      * @throws MemberAccessException
 47:      */
 48:     public static function call($_this, $name, $args)
 49:     {
 50:         $class = get_class($_this);
 51:         $isProp = self::hasProperty($class, $name);
 52:         $methods = & self::getMethods($class);
 53: 
 54:         if ($name === '') {
 55:             throw new MemberAccessException("Call to class '$class' method without name.");
 56: 
 57:         } elseif ($isProp && $_this->$name instanceof \Closure) { // closure in property
 58:             return call_user_func_array($_this->$name, $args);
 59: 
 60:         } elseif ($isProp === 'event') { // calling event handlers
 61:             if (is_array($_this->$name) || $_this->$name instanceof \Traversable) {
 62:                 foreach ($_this->$name as $handler) {
 63:                     Callback::invokeArgs($handler, $args);
 64:                 }
 65:             } elseif ($_this->$name !== NULL) {
 66:                 throw new Nette\UnexpectedValueException("Property $class::$$name must be array or NULL, " . gettype($_this->$name) ." given.");
 67:             }
 68: 
 69:         } elseif (isset($methods[$name]) && is_array($methods[$name])) { // magic @methods
 70:             list($op, $rp, $type) = $methods[$name];
 71:             if (count($args) !== ($op === 'get' ? 0 : 1)) {
 72:                 throw new Nette\InvalidArgumentException("$class::$name() expects " . ($op === 'get' ? 'no' : '1') . ' argument, ' . count($args) . ' given.');
 73: 
 74:             } elseif ($type && $args && !self::checkType($args[0], $type)) {
 75:                 throw new Nette\InvalidArgumentException("Argument passed to $class::$name() must be $type, " . gettype($args[0]) . ' given.');
 76:             }
 77: 
 78:             if ($op === 'get') {
 79:                 return $rp->getValue($_this);
 80:             } elseif ($op === 'set') {
 81:                 $rp->setValue($_this, $args[0]);
 82:             } elseif ($op === 'add') {
 83:                 $val = $rp->getValue($_this);
 84:                 $val[] = $args[0];
 85:                 $rp->setValue($_this, $val);
 86:             }
 87:             return $_this;
 88: 
 89:         } elseif ($cb = self::getExtensionMethod($class, $name)) { // extension methods
 90:             array_unshift($args, $_this);
 91:             return Callback::invokeArgs($cb, $args);
 92: 
 93:         } else {
 94:             if (method_exists($class, $name)) { // called parent::$name()
 95:                 $class = 'parent';
 96:             }
 97:             throw new MemberAccessException("Call to undefined method $class::$name().");
 98:         }
 99:     }
100: 
101: 
102:     /**
103:      * __callStatic() implementation.
104:      * @param  string
105:      * @param  string
106:      * @param  array
107:      * @return void
108:      * @throws MemberAccessException
109:      */
110:     public static function callStatic($class, $method, $args)
111:     {
112:         throw new MemberAccessException("Call to undefined static method $class::$method().");
113:     }
114: 
115: 
116:     /**
117:      * __get() implementation.
118:      * @param  object
119:      * @param  string  property name
120:      * @return mixed   property value
121:      * @throws MemberAccessException if the property is not defined.
122:      */
123:     public static function & get($_this, $name)
124:     {
125:         $class = get_class($_this);
126:         $uname = ucfirst($name);
127:         $methods = & self::getMethods($class);
128: 
129:         if ($name === '') {
130:             throw new MemberAccessException("Cannot read a class '$class' property without name.");
131: 
132:         } elseif (isset($methods[$m = 'get' . $uname]) || isset($methods[$m = 'is' . $uname])) { // property getter
133:             if ($methods[$m] === 0) {
134:                 $rm = new \ReflectionMethod($class, $m);
135:                 $methods[$m] = $rm->returnsReference();
136:             }
137:             if ($methods[$m] === TRUE) {
138:                 return $_this->$m();
139:             } else {
140:                 $val = $_this->$m();
141:                 return $val;
142:             }
143: 
144:         } elseif (isset($methods[$name])) { // public method as closure getter
145:             if (PHP_VERSION_ID >= 50400) {
146:                 $rm = new \ReflectionMethod($class, $name);
147:                 $val = $rm->getClosure($_this);
148:             } else {
149:                 $val = Callback::closure($_this, $name);
150:             }
151:             return $val;
152: 
153:         } else { // strict class
154:             $type = isset($methods['set' . $uname]) ? 'a write-only' : 'an undeclared';
155:             throw new MemberAccessException("Cannot read $type property $class::\$$name.");
156:         }
157:     }
158: 
159: 
160:     /**
161:      * __set() implementation.
162:      * @param  object
163:      * @param  string  property name
164:      * @param  mixed   property value
165:      * @return void
166:      * @throws MemberAccessException if the property is not defined or is read-only
167:      */
168:     public static function set($_this, $name, $value)
169:     {
170:         $class = get_class($_this);
171:         $uname = ucfirst($name);
172:         $methods = & self::getMethods($class);
173: 
174:         if ($name === '') {
175:             throw new MemberAccessException("Cannot write to a class '$class' property without name.");
176: 
177:         } elseif (self::hasProperty($class, $name)) { // unsetted property
178:             $_this->$name = $value;
179: 
180:         } elseif (isset($methods[$m = 'set' . $uname])) { // property setter
181:             $_this->$m($value);
182: 
183:         } else { // strict class
184:             $type = isset($methods['get' . $uname]) || isset($methods['is' . $uname])
185:                 ? 'a read-only' : 'an undeclared';
186:             throw new MemberAccessException("Cannot write to $type property $class::\$$name.");
187:         }
188:     }
189: 
190: 
191:     /**
192:      * __unset() implementation.
193:      * @param  object
194:      * @param  string  property name
195:      * @return void
196:      * @throws MemberAccessException
197:      */
198:     public static function remove($_this, $name)
199:     {
200:         $class = get_class($_this);
201:         if (!self::hasProperty($class, $name)) { // strict class
202:             throw new MemberAccessException("Cannot unset the property $class::\$$name.");
203:         }
204:     }
205: 
206: 
207:     /**
208:      * __isset() implementation.
209:      * @param  object
210:      * @param  string  property name
211:      * @return bool
212:      */
213:     public static function has($_this, $name)
214:     {
215:         $name = ucfirst($name);
216:         $methods = & self::getMethods(get_class($_this));
217:         return $name !== '' && (isset($methods['get' . $name]) || isset($methods['is' . $name]));
218:     }
219: 
220: 
221:     /**
222:      * Checks if the public non-static property exists.
223:      * @return mixed
224:      */
225:     private static function hasProperty($class, $name)
226:     {
227:         $prop = & self::$props[$class][$name];
228:         if ($prop === NULL) {
229:             $prop = FALSE;
230:             try {
231:                 $rp = new \ReflectionProperty($class, $name);
232:                 if ($rp->isPublic() && !$rp->isStatic()) {
233:                     $prop = preg_match('#^on[A-Z]#', $name) ? 'event' : TRUE;
234:                 }
235:             } catch (\ReflectionException $e) {}
236:         }
237:         return $prop;
238:     }
239: 
240: 
241:     /**
242:      * Returns array of public (static, non-static and magic) methods.
243:      * @return array
244:      */
245:     private static function & getMethods($class)
246:     {
247:         if (!isset(self::$methods[$class])) {
248:             self::$methods[$class] = array_fill_keys(get_class_methods($class), 0) + self::getMagicMethods($class);
249:             if ($parent = get_parent_class($class)) {
250:                 self::$methods[$class] += self::getMethods($parent);
251:             }
252:         }
253:         return self::$methods[$class];
254:     }
255: 
256: 
257:     /**
258:      * Returns array of magic methods defined by annotation @method.
259:      * @return array
260:      */
261:     public static function getMagicMethods($class)
262:     {
263:         $rc = new \ReflectionClass($class);
264:         preg_match_all('~^
265:             [ \t*]*  @method  [ \t]+
266:             (?: [^\s(]+  [ \t]+ )?
267:             (set|get|is|add)  ([A-Z]\w*)  [ \t]*
268:             (?: \(  [ \t]* ([^)$\s]+)  )?
269:         ()~mx', $rc->getDocComment(), $matches, PREG_SET_ORDER);
270: 
271:         $methods = array();
272:         foreach ($matches as $m) {
273:             list(, $op, $prop, $type) = $m;
274:             $name = $op . $prop;
275:             $prop = strtolower($prop[0]) . substr($prop, 1) . ($op === 'add' ? 's' : '');
276:             if ($rc->hasProperty($prop) && ($rp = $rc->getProperty($prop)) && !$rp->isStatic()) {
277:                 $rp->setAccessible(TRUE);
278:                 if ($op === 'get' || $op === 'is') {
279:                     $type = NULL; $op = 'get';
280:                 } elseif (!$type && preg_match('#@var[ \t]+(\S+)' . ($op === 'add' ? '\[\]#' : '#'), $rp->getDocComment(), $m)) {
281:                     $type = $m[1];
282:                 }
283:                 if ($rc->inNamespace() && preg_match('#^[A-Z]\w+(\[|\||\z)#', $type)) {
284:                     $type = $rc->getNamespaceName() . '\\' . $type;
285:                 }
286:                 $methods[$name] = array($op, $rp, $type);
287:             }
288:         }
289:         return $methods;
290:     }
291: 
292: 
293:     /**
294:      * Finds whether a variable is of expected type and do non-data-loss conversion.
295:      * @return bool
296:      * @internal
297:      */
298:     public static function checkType(& $val, $type)
299:     {
300:         if (strpos($type, '|') !== FALSE) {
301:             $found = NULL;
302:             foreach (explode('|', $type) as $type) {
303:                 $tmp = $val;
304:                 if (self::checkType($tmp, $type)) {
305:                     if ($val === $tmp) {
306:                         return TRUE;
307:                     }
308:                     $found[] = $tmp;
309:                 }
310:             }
311:             if ($found) {
312:                 $val = $found[0];
313:                 return TRUE;
314:             }
315:             return FALSE;
316: 
317:         } elseif (substr($type, -2) === '[]') {
318:             if (!is_array($val)) {
319:                 return FALSE;
320:             }
321:             $type = substr($type, 0, -2);
322:             $res = array();
323:             foreach ($val as $k => $v) {
324:                 if (!self::checkType($v, $type)) {
325:                     return FALSE;
326:                 }
327:                 $res[$k] = $v;
328:             }
329:             $val = $res;
330:             return TRUE;
331:         }
332: 
333:         switch (strtolower($type)) {
334:             case NULL:
335:             case 'mixed':
336:                 return TRUE;
337:             case 'bool':
338:             case 'boolean':
339:                 return ($val === NULL || is_scalar($val)) && settype($val, 'bool');
340:             case 'string':
341:                 return ($val === NULL || is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) && settype($val, 'string');
342:             case 'int':
343:             case 'integer':
344:                 return ($val === NULL || is_bool($val) || is_numeric($val)) && ((float) (int) $val === (float) $val) && settype($val, 'int');
345:             case 'float':
346:                 return ($val === NULL || is_bool($val) || is_numeric($val)) && settype($val, 'float');
347:             case 'scalar':
348:             case 'array':
349:             case 'object':
350:             case 'callable':
351:             case 'resource':
352:             case 'null':
353:                 return call_user_func("is_$type", $val);
354:             default:
355:                 return $val instanceof $type;
356:         }
357:     }
358: 
359: 
360:     /**
361:      * Adds a method to class.
362:      * @param  string
363:      * @param  string
364:      * @param  mixed   callable
365:      * @return void
366:      */
367:     public static function setExtensionMethod($class, $name, $callback)
368:     {
369:         $name = strtolower($name);
370:         self::$extMethods[$name][$class] = Callback::closure($callback);
371:         self::$extMethods[$name][''] = NULL;
372:     }
373: 
374: 
375:     /**
376:      * Returns extension method.
377:      * @param  string
378:      * @param  string
379:      * @return mixed
380:      */
381:     public static function getExtensionMethod($class, $name)
382:     {
383:         $list = & self::$extMethods[strtolower($name)];
384:         $cache = & $list[''][$class];
385:         if (isset($cache)) {
386:             return $cache;
387:         }
388: 
389:         foreach (array($class) + class_parents($class) + class_implements($class) as $cl) {
390:             if (isset($list[$cl])) {
391:                 return $cache = $list[$cl];
392:             }
393:         }
394:         return $cache = FALSE;
395:     }
396: 
397: }
398: 
Nette 2.2.6 API API documentation generated by ApiGen 2.8.0