/** * Call to undefined method. * * @param string method name * @param array arguments * @return mixed * @throws MemberAccessException */ public static function call($_this, $name, $args) { $class = new ClassReflection($_this); if ($name === '') { throw new MemberAccessException("Call to class '{$class->name}' method without name."); } // event functionality if ($class->hasEventProperty($name)) { if (is_array($list = $_this->{$name}) || $list instanceof Traversable) { foreach ($list as $handler) { fixCallback($handler); if (!is_callable($handler)) { $able = is_callable($handler, TRUE, $textual); throw new InvalidStateException("Event handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } call_user_func_array($handler, $args); } } return NULL; } // extension methods if ($cb = $class->getExtensionMethod($name)) { array_unshift($args, $_this); return call_user_func_array($cb, $args); } throw new MemberAccessException("Call to undefined method {$class->name}::{$name}()."); }
/** * Appends input string filter callback. * @param callback * @return TextBase provides a fluent interface */ public function addFilter($filter) { fixCallback($filter); if (!is_callable($filter)) { $able = is_callable($filter, TRUE, $textual); throw new InvalidArgumentException("Filter '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } $this->filters[] = $filter; return $this; }
/** * Adds a method to class. * @param string method name * @param mixed callback or closure * @return ClassReflection provides a fluent interface */ public function setExtensionMethod($name, $callback) { $l =& self::$extMethods[strtolower($name)]; fixCallback($callback); if (!is_callable($callback)) { $able = is_callable($callback, TRUE, $textual); throw new InvalidArgumentException("Extension method handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } $l[strtolower($this->getName())] = $callback; $l[''] = NULL; return $this; }
/** * Try to load the requested helper. * @param string helper name * @return callback */ public static function loader($helper) { $callback = 'Nette\\Templates\\TemplateHelpers::' . $helper; fixCallback($callback); if (is_callable($callback)) { return $callback; } $callback = 'Nette\\String::' . $helper; fixCallback($callback); if (is_callable($callback)) { return $callback; } }
/** * Add custom descriptions. * @param callback * @return void */ public static function addColophon($callback) { fixCallback($callback); if (!is_callable($callback)) { $able = is_callable($callback, TRUE, $textual); throw new InvalidArgumentException("Colophon handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } if (!in_array($callback, self::$colophons, TRUE)) { self::$colophons[] = $callback; } }
/** * Registers callback as template run-time helpers loader. * @param callback * @return void */ public function registerHelperLoader($callback) { /**/ fixCallback($callback); /**/ if (!is_callable($callback)) { $able = is_callable($callback, TRUE, $textual); throw new InvalidArgumentException("Helper loader '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } $this->helperLoaders[] = $callback; }
/** * Callback for self::macro(). */ private function cbMacro($m) { list($var, $modifiers) = $this->_cbMacro; if ($m[1]) { $callback = $m[1][0] === ':' ? array($this, substr($m[1], 1)) : $m[1]; fixCallback($callback); if (!is_callable($callback)) { $able = is_callable($callback, TRUE, $textual); throw new InvalidStateException("CurlyBrackets macro handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } return call_user_func($callback, $var, $modifiers); } else { return $var; } }
/** * Starts and initializes session data. * @throws \InvalidStateException * @return void */ public function start() { if (self::$started) { throw new InvalidStateException('Session has already been started.'); } elseif (self::$started === NULL && defined('SID')) { throw new InvalidStateException('A session had already been started by session.auto-start or session_start().'); } // additional protection against Session Hijacking & Fixation if ($this->verificationKeyGenerator) { /**/ fixCallback($this->verificationKeyGenerator); /**/ if (!is_callable($this->verificationKeyGenerator)) { $able = is_callable($this->verificationKeyGenerator, TRUE, $textual); throw new InvalidStateException("Verification key generator '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } } // start session $this->configure(self::$defaultConfig, FALSE); /*Nette\*/ Tools::tryError(); session_start(); if (Tools::catchError($msg)) { @session_write_close(); // this is needed throw new InvalidStateException($msg); } self::$started = TRUE; if ($this->regenerationNeeded) { session_regenerate_id(TRUE); $this->regenerationNeeded = FALSE; } /* structure: nette: __NT data: __NS->namespace->variable = data meta: __NM->namespace->EXP->variable = timestamp */ // initialize structures $verKey = $this->verificationKeyGenerator ? (string) call_user_func($this->verificationKeyGenerator) : ''; if (!isset($_SESSION['__NT']['V'])) { // new session $_SESSION['__NT'] = array(); $_SESSION['__NT']['C'] = 0; $_SESSION['__NT']['V'] = $verKey; } else { $saved =& $_SESSION['__NT']['V']; if ($saved === $verKey) { // verified $_SESSION['__NT']['C']++; } else { // session attack? session_regenerate_id(TRUE); $_SESSION = array(); $_SESSION['__NT']['C'] = 0; $_SESSION['__NT']['V'] = $verKey; } } // browser closing detection $browserKey = $this->getHttpRequest()->getCookie('nette-browser'); if (!$browserKey) { $browserKey = (string) lcg_value(); } $browserClosed = !isset($_SESSION['__NT']['B']) || $_SESSION['__NT']['B'] !== $browserKey; $_SESSION['__NT']['B'] = $browserKey; // resend cookie $this->sendCookie(); // process meta metadata if (isset($_SESSION['__NM'])) { $now = time(); // expire namespace variables foreach ($_SESSION['__NM'] as $namespace => $metadata) { if (isset($metadata['EXP'])) { foreach ($metadata['EXP'] as $variable => $value) { if (!is_array($value)) { $value = array($value, !$value); } // back compatibility list($time, $whenBrowserIsClosed) = $value; if ($whenBrowserIsClosed && $browserClosed || $time && $now > $time) { if ($variable === '') { // expire whole namespace unset($_SESSION['__NM'][$namespace], $_SESSION['__NS'][$namespace]); continue 2; } unset($_SESSION['__NS'][$namespace][$variable], $_SESSION['__NM'][$namespace]['EXP'][$variable]); } } } } } register_shutdown_function(array($this, 'clean')); }
/** * Gets the service object of the specified type. * @param string service name * @param array options in case service is not singleton * @return mixed */ public function getService($name, array $options = NULL) { if (!is_string($name) || $name === '') { throw new InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); } $lower = strtolower($name); if (isset($this->registry[$lower])) { // instantiated singleton if ($options) { throw new InvalidArgumentException("Service named '{$name}' is singleton and therefore can not have options."); } return $this->registry[$lower]; } elseif (isset($this->factories[$lower])) { list($factory, $singleton, $defOptions) = $this->factories[$lower]; if ($singleton && $options) { throw new InvalidArgumentException("Service named '{$name}' is singleton and therefore can not have options."); } elseif ($defOptions) { $options = $options ? $options + $defOptions : $defOptions; } if (is_string($factory) && strpos($factory, ':') === FALSE) { // class name /**/ fixNamespace($factory); /**/ if (!class_exists($factory)) { throw new AmbiguousServiceException("Cannot instantiate service '{$name}', class '{$factory}' not found."); } $service = new $factory(); if ($options && method_exists($service, 'setOptions')) { $service->setOptions($options); // TODO: better! } } else { // factory callback /**/ fixCallback($factory); /**/ if (!is_callable($factory)) { $able = is_callable($factory, TRUE, $textual); throw new AmbiguousServiceException("Cannot instantiate service '{$name}', handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } $service = call_user_func($factory, $options); if (!is_object($service)) { $able = is_callable($factory, TRUE, $textual); throw new AmbiguousServiceException("Cannot instantiate service '{$name}', value returned by '{$textual}' is not object."); } } if ($singleton) { $this->registry[$lower] = $service; unset($this->factories[$lower]); } return $service; } if ($this->parent !== NULL) { return $this->parent->getService($name, $options); } else { throw new InvalidStateException("Service '{$name}' not found."); } }
private function getCallback($rule) { $op = $rule->operation; if (is_string($op) && strncmp($op, ':', 1) === 0) { return array(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':')); } else { fixCallback($op); return $op; } }
/** * Adding method to class. * * @param string class name * @param string method name * @param mixed callback or closure * @return mixed */ public static function extensionMethod($class, $name, $callback = NULL) { /**/ if (self::$extMethods === NULL || $name === NULL) { // for backwards compatibility $list = get_defined_functions(); foreach ($list['user'] as $fce) { $pair = explode('_prototype_', $fce); if (count($pair) === 2) { self::$extMethods[$pair[1]][$pair[0]] = $fce; self::$extMethods[$pair[1]][''] = NULL; } } if ($name === NULL) { return NULL; } } /**/ $class = strtolower($class); $l =& self::$extMethods[strtolower($name)]; if ($callback !== NULL) { // works as setter /**/ fixCallback($callback); /**/ if (!is_callable($callback)) { $able = is_callable($callback, TRUE, $textual); throw new InvalidArgumentException("Extension method handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } $l[$class] = $callback; $l[''] = NULL; return NULL; } // works as getter if (empty($l)) { return FALSE; } elseif (isset($l[''][$class])) { // cached value return $l[''][$class]; } $cl = $class; do { $cl = strtolower($cl); if (isset($l[$cl])) { return $l[''][$class] = $l[$cl]; } } while (($cl = get_parent_class($cl)) !== FALSE); foreach (class_implements($class) as $cl) { $cl = strtolower($cl); if (isset($l[$cl])) { return $l[''][$class] = $l[$cl]; } } return $l[''][$class] = FALSE; }
/** * Gets the service object of the specified type. * @param string service name * @param bool throw exception if service doesn't exist? * @return mixed */ public function getService($name, $need = TRUE) { if (!is_string($name) || $name === '') { throw new InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); } $lower = strtolower($name); if (isset($this->registry[$lower])) { return $this->registry[$lower]; } elseif (isset($this->factories[$lower])) { $service = $this->factories[$lower]; if (is_string($service)) { if (substr($service, -2) === '()') { // trick to pass callback as string $service = substr($service, 0, -2); } else { fixNamespace($service); if (!class_exists($service)) { throw new AmbiguousServiceException("Cannot instantiate service '{$name}', class '{$service}' not found."); } return $this->registry[$lower] = new $service(); } } fixCallback($service); if (!is_callable($service)) { $able = is_callable($service, TRUE, $textual); throw new AmbiguousServiceException("Cannot instantiate service '{$name}', handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } return $this->registry[$lower] = call_user_func($service); } if ($this->parent !== NULL) { return $this->parent->getService($name, $need); } elseif ($need) { throw new InvalidStateException("Service '{$name}' not found."); } else { return NULL; } }
/** * Starts and initializes session data. * @throws InvalidStateException * @return void */ public function start() { if (self::$started) { throw new InvalidStateException('Session has already been started.'); } elseif (self::$started === NULL && defined('SID')) { throw new InvalidStateException('A session had already been started by session.auto-start or session_start().'); } // additional protection against Session Hijacking & Fixation if ($this->verificationKeyGenerator) { fixCallback($this->verificationKeyGenerator); if (!is_callable($this->verificationKeyGenerator)) { $able = is_callable($this->verificationKeyGenerator, TRUE, $textual); throw new InvalidStateException("Verification key generator '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } } // start session try { $this->configure($this->options); } catch (NotSupportedException $e) { // ignore? } Tools::tryError(); session_start(); if (Tools::catchError($msg)) { @session_write_close(); // this is needed throw new InvalidStateException($msg); } self::$started = TRUE; if ($this->regenerationNeeded) { session_regenerate_id(TRUE); $this->regenerationNeeded = FALSE; } /* structure: __NF: VerificationKey, Counter, BrowserKey, Data, Meta DATA: namespace->variable = data META: namespace->variable = Timestamp, Browser, Version */ // initialize structures $verKey = $this->verificationKeyGenerator ? (string) call_user_func($this->verificationKeyGenerator) : NULL; if (!isset($_SESSION['__NF']['V'])) { // new session $_SESSION['__NF'] = array(); $_SESSION['__NF']['C'] = 0; $_SESSION['__NF']['V'] = $verKey; } else { $saved =& $_SESSION['__NF']['V']; if (!$this->verificationKeyGenerator || $verKey === $saved) { // ignored or verified $_SESSION['__NF']['C']++; } else { // session attack? session_regenerate_id(TRUE); $_SESSION = array(); $_SESSION['__NF']['C'] = 0; $_SESSION['__NF']['V'] = $verKey; } } $nf =& $_SESSION['__NF']; unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']); // old structures // browser closing detection $browserKey = $this->getHttpRequest()->getCookie('nette-browser'); if (!$browserKey) { $browserKey = (string) lcg_value(); } $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey; $nf['B'] = $browserKey; // resend cookie $this->sendCookie(); // process meta metadata if (isset($nf['META'])) { $now = time(); // expire namespace variables foreach ($nf['META'] as $namespace => $metadata) { if (is_array($metadata)) { foreach ($metadata as $variable => $value) { if (!empty($value['B']) && $browserClosed || !empty($value['T']) && $now > $value['T'] || $variable !== '' && is_object($nf['DATA'][$namespace][$variable]) && (isset($value['V']) ? $value['V'] : NULL) !== ClassReflection::from($nf['DATA'][$namespace][$variable])->getAnnotation('serializationVersion')) { if ($variable === '') { // expire whole namespace unset($nf['META'][$namespace], $nf['DATA'][$namespace]); continue 2; } unset($nf['META'][$namespace][$variable], $nf['DATA'][$namespace][$variable]); } } } } } register_shutdown_function(array($this, 'clean')); }
/** * Callback for self::macro(). */ private function cbMacro($m) { list($content, $modifiers) = $this->current; if ($m[1]) { $callback = $m[1][0] === ':' ? array($this, substr($m[1], 1)) : $m[1]; /**/ fixCallback($callback); /**/ if (!is_callable($callback)) { $able = is_callable($callback, TRUE, $textual); throw new InvalidStateException("Latte macro handler '{$textual}' is not " . ($able ? 'callable.' : 'valid PHP callback.')); } return call_user_func($callback, $content, $modifiers); } else { return $content; } }