Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • ObjectMixin
  • Paginator
  • Random
  • Reflection
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

  • AssertionException
  • ImageException
  • JsonException
  • RegexpException
  • TokenizerException
  • UnknownImageFileException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  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\Utils;
  9: 
 10: use Nette;
 11: use Nette\MemberAccessException;
 12: 
 13: 
 14: /**
 15:  * Nette\Object behaviour mixin.
 16:  * @deprecated
 17:  */
 18: class ObjectMixin
 19: {
 20:     use Nette\StaticClass;
 21: 
 22:     /** @var array [name => [type => callback]] used by extension methods */
 23:     private static $extMethods = [];
 24: 
 25: 
 26:     /********************* strictness ****************d*g**/
 27: 
 28: 
 29:     /**
 30:      * @throws MemberAccessException
 31:      */
 32:     public static function strictGet($class, $name)
 33:     {
 34:         $rc = new \ReflectionClass($class);
 35:         $hint = self::getSuggestion(array_merge(
 36:             array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); }),
 37:             self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')
 38:         ), $name);
 39:         throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
 40:     }
 41: 
 42: 
 43:     /**
 44:      * @throws MemberAccessException
 45:      */
 46:     public static function strictSet($class, $name)
 47:     {
 48:         $rc = new \ReflectionClass($class);
 49:         $hint = self::getSuggestion(array_merge(
 50:             array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); }),
 51:             self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')
 52:         ), $name);
 53:         throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
 54:     }
 55: 
 56: 
 57:     /**
 58:      * @throws MemberAccessException
 59:      */
 60:     public static function strictCall($class, $method, $additionalMethods = [])
 61:     {
 62:         $hint = self::getSuggestion(array_merge(
 63:             get_class_methods($class),
 64:             self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:\S+[ \t]+)??(\w+)\(~m'),
 65:             $additionalMethods
 66:         ), $method);
 67: 
 68:         if (method_exists($class, $method)) { // called parent::$method()
 69:             $class = 'parent';
 70:         }
 71:         throw new MemberAccessException("Call to undefined method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.'));
 72:     }
 73: 
 74: 
 75:     /**
 76:      * @throws MemberAccessException
 77:      */
 78:     public static function strictStaticCall($class, $method)
 79:     {
 80:         $hint = self::getSuggestion(
 81:             array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), function ($m) { return $m->isStatic(); }),
 82:             $method
 83:         );
 84:         throw new MemberAccessException("Call to undefined static method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.'));
 85:     }
 86: 
 87: 
 88:     /********************* Nette\Object ****************d*g**/
 89: 
 90: 
 91:     /**
 92:      * __call() implementation.
 93:      * @param  object
 94:      * @param  string
 95:      * @param  array
 96:      * @return mixed
 97:      * @throws MemberAccessException
 98:      */
 99:     public static function call($_this, $name, $args)
100:     {
101:         $class = get_class($_this);
102:         $isProp = self::hasProperty($class, $name);
103: 
104:         if ($name === '') {
105:             throw new MemberAccessException("Call to class '$class' method without name.");
106: 
107:         } elseif ($isProp === 'event') { // calling event handlers
108:             if (is_array($_this->$name) || $_this->$name instanceof \Traversable) {
109:                 foreach ($_this->$name as $handler) {
110:                     Callback::invokeArgs($handler, $args);
111:                 }
112:             } elseif ($_this->$name !== null) {
113:                 throw new Nette\UnexpectedValueException("Property $class::$$name must be array or null, " . gettype($_this->$name) . ' given.');
114:             }
115: 
116:         } elseif ($isProp && $_this->$name instanceof \Closure) { // closure in property
117:             return call_user_func_array($_this->$name, $args);
118: 
119:         } elseif (($methods = &self::getMethods($class)) && isset($methods[$name]) && is_array($methods[$name])) { // magic @methods
120:             list($op, $rp, $type) = $methods[$name];
121:             if (count($args) !== ($op === 'get' ? 0 : 1)) {
122:                 throw new Nette\InvalidArgumentException("$class::$name() expects " . ($op === 'get' ? 'no' : '1') . ' argument, ' . count($args) . ' given.');
123: 
124:             } elseif ($type && $args && !self::checkType($args[0], $type)) {
125:                 throw new Nette\InvalidArgumentException("Argument passed to $class::$name() must be $type, " . gettype($args[0]) . ' given.');
126:             }
127: 
128:             if ($op === 'get') {
129:                 return $rp->getValue($_this);
130:             } elseif ($op === 'set') {
131:                 $rp->setValue($_this, $args[0]);
132:             } elseif ($op === 'add') {
133:                 $val = $rp->getValue($_this);
134:                 $val[] = $args[0];
135:                 $rp->setValue($_this, $val);
136:             }
137:             return $_this;
138: 
139:         } elseif ($cb = self::getExtensionMethod($class, $name)) { // extension methods
140:             return Callback::invoke($cb, $_this, ...$args);
141: 
142:         } else {
143:             self::strictCall($class, $name, array_keys(self::getExtensionMethods($class)));
144:         }
145:     }
146: 
147: 
148:     /**
149:      * __callStatic() implementation.
150:      * @param  string
151:      * @param  string
152:      * @param  array
153:      * @return void
154:      * @throws MemberAccessException
155:      */
156:     public static function callStatic($class, $method, $args)
157:     {
158:         self::strictStaticCall($class, $method);
159:     }
160: 
161: 
162:     /**
163:      * __get() implementation.
164:      * @param  object
165:      * @param  string  property name
166:      * @return mixed   property value
167:      * @throws MemberAccessException if the property is not defined.
168:      */
169:     public static function &get($_this, $name)
170:     {
171:         $class = get_class($_this);
172:         $uname = ucfirst($name);
173:         $methods = &self::getMethods($class);
174: 
175:         if ($name === '') {
176:             throw new MemberAccessException("Cannot read a class '$class' property without name.");
177: 
178:         } elseif (isset($methods[$m = 'get' . $uname]) || isset($methods[$m = 'is' . $uname])) { // property getter
179:             if ($methods[$m] === 0) {
180:                 $methods[$m] = (new \ReflectionMethod($class, $m))->returnsReference();
181:             }
182:             if ($methods[$m] === true) {
183:                 return $_this->$m();
184:             } else {
185:                 $val = $_this->$m();
186:                 return $val;
187:             }
188: 
189:         } elseif (isset($methods[$name])) { // public method as closure getter
190:             if (preg_match('#^(is|get|has)([A-Z]|$)#', $name) && !(new \ReflectionMethod($class, $name))->getNumberOfRequiredParameters()) {
191:                 trigger_error("Did you forget parentheses after $name" . self::getSource() . '?', E_USER_WARNING);
192:             }
193:             $val = Callback::closure($_this, $name);
194:             return $val;
195: 
196:         } elseif (isset($methods['set' . $uname])) { // property getter
197:             throw new MemberAccessException("Cannot read a write-only property $class::\$$name.");
198: 
199:         } else {
200:             self::strictGet($class, $name);
201:         }
202:     }
203: 
204: 
205:     /**
206:      * __set() implementation.
207:      * @param  object
208:      * @param  string  property name
209:      * @param  mixed   property value
210:      * @return void
211:      * @throws MemberAccessException if the property is not defined or is read-only
212:      */
213:     public static function set($_this, $name, $value)
214:     {
215:         $class = get_class($_this);
216:         $uname = ucfirst($name);
217:         $methods = &self::getMethods($class);
218: 
219:         if ($name === '') {
220:             throw new MemberAccessException("Cannot write to a class '$class' property without name.");
221: 
222:         } elseif (self::hasProperty($class, $name)) { // unsetted property
223:             $_this->$name = $value;
224: 
225:         } elseif (isset($methods[$m = 'set' . $uname])) { // property setter
226:             $_this->$m($value);
227: 
228:         } elseif (isset($methods['get' . $uname]) || isset($methods['is' . $uname])) { // property setter
229:             throw new MemberAccessException("Cannot write to a read-only property $class::\$$name.");
230: 
231:         } else {
232:             self::strictSet($class, $name);
233:         }
234:     }
235: 
236: 
237:     /**
238:      * __unset() implementation.
239:      * @param  object
240:      * @param  string  property name
241:      * @return void
242:      * @throws MemberAccessException
243:      */
244:     public static function remove($_this, $name)
245:     {
246:         $class = get_class($_this);
247:         if (!self::hasProperty($class, $name)) {
248:             throw new MemberAccessException("Cannot unset the property $class::\$$name.");
249:         }
250:     }
251: 
252: 
253:     /**
254:      * __isset() implementation.
255:      * @param  object
256:      * @param  string  property name
257:      * @return bool
258:      */
259:     public static function has($_this, $name)
260:     {
261:         $name = ucfirst($name);
262:         $methods = &self::getMethods(get_class($_this));
263:         return $name !== '' && (isset($methods['get' . $name]) || isset($methods['is' . $name]));
264:     }
265: 
266: 
267:     /********************* magic @properties ****************d*g**/
268: 
269: 
270:     /**
271:      * Returns array of magic properties defined by annotation @property.
272:      * @return array of [name => bit mask]
273:      */
274:     public static function getMagicProperties($class)
275:     {
276:         static $cache;
277:         $props = &$cache[$class];
278:         if ($props !== null) {
279:             return $props;
280:         }
281: 
282:         $rc = new \ReflectionClass($class);
283:         preg_match_all(
284:             '~^  [ \t*]*  @property(|-read|-write)  [ \t]+  [^\s$]+  [ \t]+  \$  (\w+)  ()~mx',
285:             (string) $rc->getDocComment(), $matches, PREG_SET_ORDER
286:         );
287: 
288:         $props = [];
289:         foreach ($matches as list(, $type, $name)) {
290:             $uname = ucfirst($name);
291:             $write = $type !== '-read'
292:                 && $rc->hasMethod($nm = 'set' . $uname)
293:                 && ($rm = $rc->getMethod($nm)) && $rm->getName() === $nm && !$rm->isPrivate() && !$rm->isStatic();
294:             $read = $type !== '-write'
295:                 && ($rc->hasMethod($nm = 'get' . $uname) || $rc->hasMethod($nm = 'is' . $uname))
296:                 && ($rm = $rc->getMethod($nm)) && $rm->getName() === $nm && !$rm->isPrivate() && !$rm->isStatic();
297: 
298:             if ($read || $write) {
299:                 $props[$name] = $read << 0 | ($nm[0] === 'g') << 1 | $rm->returnsReference() << 2 | $write << 3;
300:             }
301:         }
302: 
303:         foreach ($rc->getTraits() as $trait) {
304:             $props += self::getMagicProperties($trait->getName());
305:         }
306: 
307:         if ($parent = get_parent_class($class)) {
308:             $props += self::getMagicProperties($parent);
309:         }
310:         return $props;
311:     }
312: 
313: 
314:     /** @internal */
315:     public static function getMagicProperty($class, $name)
316:     {
317:         $props = self::getMagicProperties($class);
318:         return isset($props[$name]) ? $props[$name] : null;
319:     }
320: 
321: 
322:     /********************* magic @methods ****************d*g**/
323: 
324: 
325:     /**
326:      * Returns array of magic methods defined by annotation @method.
327:      * @return array
328:      */
329:     public static function getMagicMethods($class)
330:     {
331:         $rc = new \ReflectionClass($class);
332:         preg_match_all('~^
333:             [ \t*]*  @method  [ \t]+
334:             (?: [^\s(]+  [ \t]+ )?
335:             (set|get|is|add)  ([A-Z]\w*)
336:             (?: ([ \t]* \()  [ \t]* ([^)$\s]*)  )?
337:         ()~mx', (string) $rc->getDocComment(), $matches, PREG_SET_ORDER);
338: 
339:         $methods = [];
340:         foreach ($matches as list(, $op, $prop, $bracket, $type)) {
341:             if ($bracket !== '(') {
342:                 trigger_error("Bracket must be immediately after @method $op$prop() in class $class.", E_USER_WARNING);
343:             }
344:             $name = $op . $prop;
345:             $prop = strtolower($prop[0]) . substr($prop, 1) . ($op === 'add' ? 's' : '');
346:             if ($rc->hasProperty($prop) && ($rp = $rc->getProperty($prop)) && !$rp->isStatic()) {
347:                 $rp->setAccessible(true);
348:                 if ($op === 'get' || $op === 'is') {
349:                     $type = null;
350:                     $op = 'get';
351:                 } elseif (!$type && preg_match('#@var[ \t]+(\S+)' . ($op === 'add' ? '\[\]#' : '#'), (string) $rp->getDocComment(), $m)) {
352:                     $type = $m[1];
353:                 }
354:                 if ($rc->inNamespace() && preg_match('#^[A-Z]\w+(\[|\||\z)#', (string) $type)) {
355:                     $type = $rc->getNamespaceName() . '\\' . $type;
356:                 }
357:                 $methods[$name] = [$op, $rp, $type];
358:             }
359:         }
360:         return $methods;
361:     }
362: 
363: 
364:     /**
365:      * Finds whether a variable is of expected type and do non-data-loss conversion.
366:      * @return bool
367:      * @internal
368:      */
369:     public static function checkType(&$val, $type)
370:     {
371:         if (strpos($type, '|') !== false) {
372:             $found = null;
373:             foreach (explode('|', $type) as $type) {
374:                 $tmp = $val;
375:                 if (self::checkType($tmp, $type)) {
376:                     if ($val === $tmp) {
377:                         return true;
378:                     }
379:                     $found[] = $tmp;
380:                 }
381:             }
382:             if ($found) {
383:                 $val = $found[0];
384:                 return true;
385:             }
386:             return false;
387: 
388:         } elseif (substr($type, -2) === '[]') {
389:             if (!is_array($val)) {
390:                 return false;
391:             }
392:             $type = substr($type, 0, -2);
393:             $res = [];
394:             foreach ($val as $k => $v) {
395:                 if (!self::checkType($v, $type)) {
396:                     return false;
397:                 }
398:                 $res[$k] = $v;
399:             }
400:             $val = $res;
401:             return true;
402:         }
403: 
404:         switch (strtolower($type)) {
405:             case null:
406:             case 'mixed':
407:                 return true;
408:             case 'bool':
409:             case 'boolean':
410:                 return ($val === null || is_scalar($val)) && settype($val, 'bool');
411:             case 'string':
412:                 return ($val === null || is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) && settype($val, 'string');
413:             case 'int':
414:             case 'integer':
415:                 return ($val === null || is_bool($val) || is_numeric($val)) && ((float) (int) $val === (float) $val) && settype($val, 'int');
416:             case 'float':
417:                 return ($val === null || is_bool($val) || is_numeric($val)) && settype($val, 'float');
418:             case 'scalar':
419:             case 'array':
420:             case 'object':
421:             case 'callable':
422:             case 'resource':
423:             case 'null':
424:                 return call_user_func("is_$type", $val);
425:             default:
426:                 return $val instanceof $type;
427:         }
428:     }
429: 
430: 
431:     /********************* extension methods ****************d*g**/
432: 
433: 
434:     /**
435:      * Adds a method to class.
436:      * @param  string
437:      * @param  string
438:      * @param  mixed   callable
439:      * @return void
440:      */
441:     public static function setExtensionMethod($class, $name, $callback)
442:     {
443:         $name = strtolower($name);
444:         self::$extMethods[$name][$class] = Callback::check($callback);
445:         self::$extMethods[$name][''] = null;
446:     }
447: 
448: 
449:     /**
450:      * Returns extension method.
451:      * @param  string
452:      * @param  string
453:      * @return mixed
454:      */
455:     public static function getExtensionMethod($class, $name)
456:     {
457:         $list = &self::$extMethods[strtolower($name)];
458:         $cache = &$list[''][$class];
459:         if (isset($cache)) {
460:             return $cache;
461:         }
462: 
463:         foreach ([$class] + class_parents($class) + class_implements($class) as $cl) {
464:             if (isset($list[$cl])) {
465:                 return $cache = $list[$cl];
466:             }
467:         }
468:         return $cache = false;
469:     }
470: 
471: 
472:     /**
473:      * Returns extension methods.
474:      * @param  string
475:      * @return array
476:      */
477:     public static function getExtensionMethods($class)
478:     {
479:         $res = [];
480:         foreach (array_keys(self::$extMethods) as $name) {
481:             if ($cb = self::getExtensionMethod($class, $name)) {
482:                 $res[$name] = $cb;
483:             }
484:         }
485:         return $res;
486:     }
487: 
488: 
489:     /********************* utilities ****************d*g**/
490: 
491: 
492:     /**
493:      * Finds the best suggestion (for 8-bit encoding).
494:      * @return string|null
495:      * @internal
496:      */
497:     public static function getSuggestion(array $possibilities, $value)
498:     {
499:         $norm = preg_replace($re = '#^(get|set|has|is|add)(?=[A-Z])#', '', $value);
500:         $best = null;
501:         $min = (strlen($value) / 4 + 1) * 10 + .1;
502:         foreach (array_unique($possibilities, SORT_REGULAR) as $item) {
503:             $item = $item instanceof \Reflector ? $item->getName() : $item;
504:             if ($item !== $value && (
505:                 ($len = levenshtein($item, $value, 10, 11, 10)) < $min
506:                 || ($len = levenshtein(preg_replace($re, '', $item), $norm, 10, 11, 10) + 20) < $min
507:             )) {
508:                 $min = $len;
509:                 $best = $item;
510:             }
511:         }
512:         return $best;
513:     }
514: 
515: 
516:     private static function parseFullDoc(\ReflectionClass $rc, $pattern)
517:     {
518:         do {
519:             $doc[] = $rc->getDocComment();
520:             $traits = $rc->getTraits();
521:             while ($trait = array_pop($traits)) {
522:                 $doc[] = $trait->getDocComment();
523:                 $traits += $trait->getTraits();
524:             }
525:         } while ($rc = $rc->getParentClass());
526:         return preg_match_all($pattern, implode($doc), $m) ? $m[1] : [];
527:     }
528: 
529: 
530:     /**
531:      * Checks if the public non-static property exists.
532:      * @return bool|'event'
533:      * @internal
534:      */
535:     public static function hasProperty($class, $name)
536:     {
537:         static $cache;
538:         $prop = &$cache[$class][$name];
539:         if ($prop === null) {
540:             $prop = false;
541:             try {
542:                 $rp = new \ReflectionProperty($class, $name);
543:                 if ($rp->isPublic() && !$rp->isStatic()) {
544:                     $prop = $name >= 'onA' && $name < 'on_' ? 'event' : true;
545:                 }
546:             } catch (\ReflectionException $e) {
547:             }
548:         }
549:         return $prop;
550:     }
551: 
552: 
553:     /**
554:      * Returns array of public (static, non-static and magic) methods.
555:      * @return array
556:      * @internal
557:      */
558:     public static function &getMethods($class)
559:     {
560:         static $cache;
561:         if (!isset($cache[$class])) {
562:             $cache[$class] = array_fill_keys(get_class_methods($class), 0) + self::getMagicMethods($class);
563:             if ($parent = get_parent_class($class)) {
564:                 $cache[$class] += self::getMethods($parent);
565:             }
566:         }
567:         return $cache[$class];
568:     }
569: 
570: 
571:     /** @internal */
572:     public static function getSource()
573:     {
574:         foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $item) {
575:             if (isset($item['file']) && dirname($item['file']) !== __DIR__) {
576:                 return " in $item[file]:$item[line]";
577:             }
578:         }
579:     }
580: }
581: 
Nette 2.4-20180206 API API documentation generated by ApiGen 2.8.0