Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • none

Classes

  • ArrayHash
  • ArrayList
  • Callback
  • Configurator
  • DateTime
  • Environment
  • Framework
  • FreezableObject
  • Image
  • Object
  • ObjectMixin

Interfaces

  • IFreezable

Exceptions

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