PageRenderTime 157ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 2ms

/libs/Nette/loader.php

https://github.com/Edke/Nette-addDynamic
PHP | 19983 lines | 16680 code | 3295 blank | 8 comment | 1933 complexity | 760910a90a941effafb0a54c47873564 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php //netteloader=Nette\Framework
  2. namespace {
  3. /**
  4. * Nette Framework (version 2.0-dev released on 2011-06-13, http://nette.org)
  5. *
  6. * Copyright (c) 2004, 2011 David Grudl (http://davidgrudl.com)
  7. *
  8. * For the full copyright and license information, please view
  9. * the file license.txt that was distributed with this source code.
  10. */
  11. error_reporting(E_ALL | E_STRICT);
  12. @set_magic_quotes_runtime(FALSE);
  13. iconv_set_encoding('internal_encoding', 'UTF-8');
  14. extension_loaded('mbstring') && mb_internal_encoding('UTF-8');
  15. @header('X-Powered-By: Nette Framework');
  16. define('NETTE', TRUE);
  17. define('NETTE_DIR', __DIR__);
  18. define('NETTE_VERSION_ID', 20000);
  19. define('NETTE_PACKAGE', '5.3');
  20. }
  21. namespace Nette\Diagnostics {
  22. use Nette;
  23. interface IBarPanel
  24. {
  25. function getTab();
  26. function getPanel();
  27. }
  28. }
  29. namespace Nette\Application {
  30. use Nette;
  31. interface IPresenter
  32. {
  33. function run(Request $request);
  34. }
  35. interface IPresenterFactory
  36. {
  37. function getPresenterClass(& $name);
  38. function createPresenter($name);
  39. }
  40. interface IResponse
  41. {
  42. function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse);
  43. }
  44. interface IRouter
  45. {
  46. const ONE_WAY = 1;
  47. const SECURED = 2;
  48. function match(Nette\Http\IRequest $httpRequest);
  49. function constructUrl(Request $appRequest, Nette\Http\Url $refUrl);
  50. }
  51. }
  52. namespace Nette {
  53. use Nette;
  54. interface IFreezable
  55. {
  56. function freeze();
  57. function isFrozen();
  58. }
  59. }
  60. namespace Nette\ComponentModel {
  61. use Nette;
  62. interface IComponent
  63. {
  64. const NAME_SEPARATOR = '-';
  65. function getName();
  66. function getParent();
  67. function setParent(IContainer $parent = NULL, $name = NULL);
  68. }
  69. interface IContainer extends IComponent
  70. {
  71. function addComponent(IComponent $component, $name);
  72. function removeComponent(IComponent $component);
  73. function getComponent($name);
  74. function getComponents($deep = FALSE, $filterType = NULL);
  75. }
  76. }
  77. namespace Nette\Application\UI {
  78. use Nette;
  79. interface ISignalReceiver
  80. {
  81. function signalReceived($signal);
  82. }
  83. interface IStatePersistent
  84. {
  85. function loadState(array $params);
  86. function saveState(array & $params);
  87. }
  88. interface IPartiallyRenderable extends IRenderable
  89. {
  90. }
  91. interface IRenderable
  92. {
  93. function invalidateControl();
  94. function isControlInvalid();
  95. }
  96. }
  97. namespace Nette\Caching {
  98. use Nette;
  99. interface IStorage
  100. {
  101. function read($key);
  102. function write($key, $data, array $dependencies);
  103. function remove($key);
  104. function clean(array $conds);
  105. }
  106. }
  107. namespace Nette\Caching\Storages {
  108. use Nette;
  109. interface IJournal
  110. {
  111. function write($key, array $dependencies);
  112. function clean(array $conditions);
  113. }
  114. }
  115. namespace Nette\Config {
  116. use Nette;
  117. interface IAdapter
  118. {
  119. static function load($file);
  120. static function save($config, $file);
  121. }
  122. }
  123. namespace Nette\Database {
  124. use Nette;
  125. interface ISupplementalDriver
  126. {
  127. function delimite($name);
  128. function formatDateTime(\DateTime $value);
  129. function formatLike($value, $pos);
  130. function applyLimit(&$sql, $limit, $offset);
  131. function normalizeRow($row, $statement);
  132. }
  133. }
  134. namespace Nette\DI {
  135. use Nette;
  136. interface IContainer
  137. {
  138. function addService($name, $service);
  139. function getService($name);
  140. function removeService($name);
  141. function hasService($name);
  142. }
  143. interface IServiceBuilder
  144. {
  145. function createService(IContainer $container);
  146. }
  147. }
  148. namespace Nette\Forms {
  149. use Nette;
  150. interface IControl
  151. {
  152. function loadHttpData();
  153. function setValue($value);
  154. function getValue();
  155. function getRules();
  156. function getErrors();
  157. function isDisabled();
  158. function translate($s, $count = NULL);
  159. }
  160. interface ISubmitterControl extends IControl
  161. {
  162. function isSubmittedBy();
  163. function getValidationScope();
  164. }
  165. interface IFormRenderer
  166. {
  167. function render(Form $form);
  168. }
  169. }
  170. namespace Nette\Http {
  171. use Nette;
  172. interface IRequest
  173. {
  174. const
  175. GET = 'GET',
  176. POST = 'POST',
  177. HEAD = 'HEAD',
  178. PUT = 'PUT',
  179. DELETE = 'DELETE';
  180. function getUrl();
  181. function getQuery($key = NULL, $default = NULL);
  182. function getPost($key = NULL, $default = NULL);
  183. function getFile($key);
  184. function getFiles();
  185. function getCookie($key, $default = NULL);
  186. function getCookies();
  187. function getMethod();
  188. function isMethod($method);
  189. function getHeader($header, $default = NULL);
  190. function getHeaders();
  191. function isSecured();
  192. function isAjax();
  193. function getRemoteAddress();
  194. function getRemoteHost();
  195. }
  196. interface IResponse
  197. {
  198. const PERMANENT = 2116333333;
  199. const BROWSER = 0;
  200. const
  201. S200_OK = 200,
  202. S204_NO_CONTENT = 204,
  203. S300_MULTIPLE_CHOICES = 300,
  204. S301_MOVED_PERMANENTLY = 301,
  205. S302_FOUND = 302,
  206. S303_SEE_OTHER = 303,
  207. S303_POST_GET = 303,
  208. S304_NOT_MODIFIED = 304,
  209. S307_TEMPORARY_REDIRECT= 307,
  210. S400_BAD_REQUEST = 400,
  211. S401_UNAUTHORIZED = 401,
  212. S403_FORBIDDEN = 403,
  213. S404_NOT_FOUND = 404,
  214. S405_METHOD_NOT_ALLOWED = 405,
  215. S410_GONE = 410,
  216. S500_INTERNAL_SERVER_ERROR = 500,
  217. S501_NOT_IMPLEMENTED = 501,
  218. S503_SERVICE_UNAVAILABLE = 503;
  219. function setCode($code);
  220. function getCode();
  221. function setHeader($name, $value);
  222. function addHeader($name, $value);
  223. function setContentType($type, $charset = NULL);
  224. function redirect($url, $code = self::S302_FOUND);
  225. function setExpiration($seconds);
  226. function isSent();
  227. function getHeaders();
  228. function setCookie($name, $value, $expire, $path = NULL, $domain = NULL, $secure = NULL, $httpOnly = NULL);
  229. function deleteCookie($name, $path = NULL, $domain = NULL, $secure = NULL);
  230. }
  231. interface ISessionStorage
  232. {
  233. function open($savePath, $sessionName);
  234. function close();
  235. function read($id);
  236. function write($id, $data);
  237. function remove($id);
  238. function clean($maxlifetime);
  239. }
  240. interface IUser
  241. {
  242. function login();
  243. function logout($clearIdentity = FALSE);
  244. function isLoggedIn();
  245. function getIdentity();
  246. function setAuthenticator(Nette\Security\IAuthenticator $handler);
  247. function getAuthenticator();
  248. function setNamespace($namespace);
  249. function getNamespace();
  250. function getRoles();
  251. function isInRole($role);
  252. function isAllowed();
  253. function setAuthorizator(Nette\Security\IAuthorizator $handler);
  254. function getAuthorizator();
  255. }
  256. }
  257. namespace Nette\Latte {
  258. use Nette;
  259. interface IMacro
  260. {
  261. function initialize();
  262. function finalize();
  263. function nodeOpened(MacroNode $node);
  264. function nodeClosed(MacroNode $node);
  265. }
  266. }
  267. namespace Nette\Localization {
  268. use Nette;
  269. interface ITranslator
  270. {
  271. function translate($message, $count = NULL);
  272. }
  273. }
  274. namespace Nette\Mail {
  275. use Nette;
  276. interface IMailer
  277. {
  278. function send(Message $mail);
  279. }
  280. }
  281. namespace Nette\Reflection {
  282. use Nette;
  283. interface IAnnotation
  284. {
  285. function __construct(array $values);
  286. }
  287. }
  288. namespace Nette\Security {
  289. use Nette;
  290. interface IAuthenticator
  291. {
  292. const USERNAME = 0,
  293. PASSWORD = 1;
  294. const IDENTITY_NOT_FOUND = 1,
  295. INVALID_CREDENTIAL = 2,
  296. FAILURE = 3,
  297. NOT_APPROVED = 4;
  298. function authenticate(array $credentials);
  299. }
  300. interface IAuthorizator
  301. {
  302. const ALL = NULL;
  303. const ALLOW = TRUE;
  304. const DENY = FALSE;
  305. function isAllowed($role, $resource, $privilege);
  306. }
  307. interface IIdentity
  308. {
  309. function getId();
  310. function getRoles();
  311. }
  312. interface IResource
  313. {
  314. function getResourceId();
  315. }
  316. interface IRole
  317. {
  318. function getRoleId();
  319. }
  320. }
  321. namespace Nette\Templating {
  322. use Nette;
  323. interface ITemplate
  324. {
  325. function render();
  326. }
  327. interface IFileTemplate extends ITemplate
  328. {
  329. function setFile($file);
  330. function getFile();
  331. }
  332. }
  333. namespace Nette {
  334. use Nette;
  335. class ArgumentOutOfRangeException extends \InvalidArgumentException
  336. {
  337. }
  338. class InvalidStateException extends \RuntimeException
  339. {
  340. }
  341. class NotImplementedException extends \LogicException
  342. {
  343. }
  344. class NotSupportedException extends \LogicException
  345. {
  346. }
  347. class DeprecatedException extends NotSupportedException
  348. {
  349. }
  350. class MemberAccessException extends \LogicException
  351. {
  352. }
  353. class IOException extends \RuntimeException
  354. {
  355. }
  356. class FileNotFoundException extends IOException
  357. {
  358. }
  359. class DirectoryNotFoundException extends IOException
  360. {
  361. }
  362. class InvalidArgumentException extends \InvalidArgumentException
  363. {
  364. }
  365. class OutOfRangeException extends \OutOfRangeException
  366. {
  367. }
  368. class UnexpectedValueException extends \UnexpectedValueException
  369. {
  370. }
  371. class StaticClassException extends \LogicException
  372. {
  373. }
  374. class FatalErrorException extends \ErrorException
  375. {
  376. function __construct($message, $code, $severity, $file, $line, $context)
  377. {
  378. parent::__construct($message, $code, $severity, $file, $line);
  379. $this->context = $context;
  380. }
  381. }
  382. abstract class Object
  383. {
  384. static function getReflection()
  385. {
  386. return new Reflection\ClassType(get_called_class());
  387. }
  388. function __call($name, $args)
  389. {
  390. return ObjectMixin::call($this, $name, $args);
  391. }
  392. static function __callStatic($name, $args)
  393. {
  394. return ObjectMixin::callStatic(get_called_class(), $name, $args);
  395. }
  396. static function extensionMethod($name, $callback = NULL)
  397. {
  398. if (strpos($name, '::') === FALSE) {
  399. $class = get_called_class();
  400. } else {
  401. list($class, $name) = explode('::', $name);
  402. }
  403. $class = new Reflection\ClassType($class);
  404. if ($callback === NULL) {
  405. return $class->getExtensionMethod($name);
  406. } else {
  407. $class->setExtensionMethod($name, $callback);
  408. }
  409. }
  410. function &__get($name)
  411. {
  412. return ObjectMixin::get($this, $name);
  413. }
  414. function __set($name, $value)
  415. {
  416. return ObjectMixin::set($this, $name, $value);
  417. }
  418. function __isset($name)
  419. {
  420. return ObjectMixin::has($this, $name);
  421. }
  422. function __unset($name)
  423. {
  424. ObjectMixin::remove($this, $name);
  425. }
  426. }
  427. }
  428. namespace Nette\Utils {
  429. use Nette;
  430. final class LimitedScope
  431. {
  432. private static $vars;
  433. final function __construct()
  434. {
  435. throw new Nette\StaticClassException;
  436. }
  437. static function evaluate()
  438. {
  439. if (func_num_args() > 1) {
  440. self::$vars = func_get_arg(1);
  441. extract(self::$vars);
  442. }
  443. $res = eval('?>' . func_get_arg(0));
  444. if ($res === FALSE && ($error = error_get_last()) && $error['type'] === E_PARSE) {
  445. throw new Nette\FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL);
  446. }
  447. return $res;
  448. }
  449. static function load()
  450. {
  451. if (func_num_args() > 1) {
  452. self::$vars = func_get_arg(1);
  453. extract(self::$vars);
  454. }
  455. return include func_get_arg(0);
  456. }
  457. }
  458. }
  459. namespace Nette\Loaders {
  460. use Nette;
  461. abstract class AutoLoader extends Nette\Object
  462. {
  463. static private $loaders = array();
  464. public static $count = 0;
  465. final static function load($type)
  466. {
  467. foreach (func_get_args() as $type) {
  468. if (!class_exists($type)) {
  469. throw new Nette\InvalidStateException("Unable to load class or interface '$type'.");
  470. }
  471. }
  472. }
  473. final static function getLoaders()
  474. {
  475. return array_values(self::$loaders);
  476. }
  477. function register()
  478. {
  479. if (!function_exists('spl_autoload_register')) {
  480. throw new Nette\NotSupportedException('spl_autoload does not exist in this PHP installation.');
  481. }
  482. spl_autoload_register(array($this, 'tryLoad'));
  483. self::$loaders[spl_object_hash($this)] = $this;
  484. }
  485. function unregister()
  486. {
  487. unset(self::$loaders[spl_object_hash($this)]);
  488. return spl_autoload_unregister(array($this, 'tryLoad'));
  489. }
  490. abstract function tryLoad($type);
  491. }
  492. }
  493. namespace Nette\Diagnostics {
  494. use Nette;
  495. final class Debugger
  496. {
  497. public static $productionMode;
  498. public static $consoleMode;
  499. public static $time;
  500. private static $ajaxDetected;
  501. public static $source;
  502. public static $editor = 'editor://open/?file=%file&line=%line';
  503. public static $maxDepth = 3;
  504. public static $maxLen = 150;
  505. public static $showLocation = FALSE;
  506. const DEVELOPMENT = FALSE,
  507. PRODUCTION = TRUE,
  508. DETECT = NULL;
  509. public static $blueScreen;
  510. public static $strictMode = FALSE;
  511. public static $scream = FALSE;
  512. public static $onFatalError = array();
  513. private static $enabled = FALSE;
  514. private static $lastError = FALSE;
  515. public static $logger;
  516. public static $fireLogger;
  517. public static $logDirectory;
  518. public static $email;
  519. public static $mailer;
  520. public static $emailSnooze;
  521. public static $bar;
  522. private static $errorPanel;
  523. private static $dumpPanel;
  524. const DEBUG = 'debug',
  525. INFO = 'info',
  526. WARNING = 'warning',
  527. ERROR = 'error',
  528. CRITICAL = 'critical';
  529. final function __construct()
  530. {
  531. throw new Nette\StaticClassException;
  532. }
  533. static function _init()
  534. {
  535. self::$time = microtime(TRUE);
  536. self::$consoleMode = PHP_SAPI === 'cli';
  537. self::$productionMode = self::DETECT;
  538. if (self::$consoleMode) {
  539. self::$source = empty($_SERVER['argv']) ? 'cli' : 'cli: ' . implode(' ', $_SERVER['argv']);
  540. } else {
  541. self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
  542. if (isset($_SERVER['REQUEST_URI'])) {
  543. self::$source = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://')
  544. . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''))
  545. . $_SERVER['REQUEST_URI'];
  546. }
  547. }
  548. self::$logger = new Logger;
  549. self::$logDirectory = & self::$logger->directory;
  550. self::$email = & self::$logger->email;
  551. self::$mailer = & self::$logger->mailer;
  552. self::$emailSnooze = & Logger::$emailSnooze;
  553. self::$fireLogger = new FireLogger;
  554. self::$blueScreen = new BlueScreen;
  555. self::$blueScreen->addPanel(function($e) {
  556. if ($e instanceof Nette\Templating\FilterException) {
  557. return array(
  558. 'tab' => 'Template',
  559. 'panel' => '<p><b>File:</b> ' . Helpers::editorLink($e->sourceFile, $e->sourceLine)
  560. . '&nbsp; <b>Line:</b> ' . ($e->sourceLine ? $e->sourceLine : 'n/a') . '</p>'
  561. . ($e->sourceLine ? '<pre>' . BlueScreen::highlightFile($e->sourceFile, $e->sourceLine) . '</pre>' : '')
  562. );
  563. }
  564. });
  565. self::$bar = new Bar;
  566. self::$bar->addPanel(new DefaultBarPanel('time'));
  567. self::$bar->addPanel(new DefaultBarPanel('memory'));
  568. self::$bar->addPanel(self::$errorPanel = new DefaultBarPanel('errors'));
  569. self::$bar->addPanel(self::$dumpPanel = new DefaultBarPanel('dumps'));
  570. }
  571. static function enable($mode = NULL, $logDirectory = NULL, $email = NULL)
  572. {
  573. error_reporting(E_ALL | E_STRICT);
  574. if (is_bool($mode)) {
  575. self::$productionMode = $mode;
  576. } elseif (is_string($mode)) {
  577. $mode = preg_split('#[,\s]+#', "$mode 127.0.0.1 ::1");
  578. }
  579. if (is_array($mode)) {
  580. self::$productionMode = !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $mode, TRUE);
  581. }
  582. if (self::$productionMode === self::DETECT) {
  583. if (class_exists('Nette\Environment')) {
  584. self::$productionMode = Nette\Environment::isProduction();
  585. } elseif (isset($_SERVER['SERVER_ADDR']) || isset($_SERVER['LOCAL_ADDR'])) {
  586. $addrs = array();
  587. if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  588. $addrs = preg_split('#,\s*#', $_SERVER['HTTP_X_FORWARDED_FOR']);
  589. }
  590. if (isset($_SERVER['REMOTE_ADDR'])) {
  591. $addrs[] = $_SERVER['REMOTE_ADDR'];
  592. }
  593. $addrs[] = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
  594. self::$productionMode = FALSE;
  595. foreach ($addrs as $addr) {
  596. $oct = explode('.', $addr);
  597. if ($addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
  598. && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')))
  599. ) {
  600. self::$productionMode = TRUE;
  601. break;
  602. }
  603. }
  604. } else {
  605. self::$productionMode = !self::$consoleMode;
  606. }
  607. }
  608. if (is_string($logDirectory)) {
  609. self::$logDirectory = realpath($logDirectory);
  610. if (self::$logDirectory === FALSE) {
  611. throw new Nette\DirectoryNotFoundException("Directory '$logDirectory' is not found.");
  612. }
  613. } elseif ($logDirectory === FALSE) {
  614. self::$logDirectory = FALSE;
  615. } elseif (self::$logDirectory === NULL) {
  616. self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log' : getcwd() . '/log';
  617. }
  618. if (self::$logDirectory) {
  619. ini_set('error_log', self::$logDirectory . '/php_error.log');
  620. }
  621. if (function_exists('ini_set')) {
  622. ini_set('display_errors', !self::$productionMode);
  623. ini_set('html_errors', FALSE);
  624. ini_set('log_errors', FALSE);
  625. } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) {
  626. throw new Nette\NotSupportedException('Function ini_set() must be enabled.');
  627. }
  628. if ($email) {
  629. if (!is_string($email)) {
  630. throw new Nette\InvalidArgumentException('Email address must be a string.');
  631. }
  632. self::$email = $email;
  633. }
  634. if (!defined('E_DEPRECATED')) {
  635. define('E_DEPRECATED', 8192);
  636. }
  637. if (!defined('E_USER_DEPRECATED')) {
  638. define('E_USER_DEPRECATED', 16384);
  639. }
  640. if (!self::$enabled) {
  641. register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
  642. set_exception_handler(array(__CLASS__, '_exceptionHandler'));
  643. set_error_handler(array(__CLASS__, '_errorHandler'));
  644. self::$enabled = TRUE;
  645. }
  646. }
  647. static function isEnabled()
  648. {
  649. return self::$enabled;
  650. }
  651. static function log($message, $priority = self::INFO)
  652. {
  653. if (self::$logDirectory === FALSE) {
  654. return;
  655. } elseif (!self::$logDirectory) {
  656. throw new Nette\InvalidStateException('Logging directory is not specified in Nette\Diagnostics\Debugger::$logDirectory.');
  657. }
  658. if ($message instanceof \Exception) {
  659. $exception = $message;
  660. $message = "PHP Fatal error: "
  661. . ($message instanceof Nette\FatalErrorException
  662. ? $exception->getMessage()
  663. : "Uncaught exception " . get_class($exception) . " with message '" . $exception->getMessage() . "'")
  664. . " in " . $exception->getFile() . ":" . $exception->getLine();
  665. $hash = md5($exception );
  666. $exceptionFilename = "exception " . @date('Y-m-d H-i-s') . " $hash.html";
  667. foreach (new \DirectoryIterator(self::$logDirectory) as $entry) {
  668. if (strpos($entry, $hash)) {
  669. $exceptionFilename = NULL; break;
  670. }
  671. }
  672. }
  673. self::$logger->log(array(
  674. @date('[Y-m-d H-i-s]'),
  675. $message,
  676. self::$source ? ' @ ' . self::$source : NULL,
  677. !empty($exceptionFilename) ? ' @@ ' . $exceptionFilename : NULL
  678. ), $priority);
  679. if (!empty($exceptionFilename) && $logHandle = @fopen(self::$logDirectory . '/'. $exceptionFilename, 'w')) {
  680. ob_start();
  681. ob_start(function($buffer) use($logHandle) { fwrite($logHandle, $buffer); }, 1);
  682. self::$blueScreen->render($exception);
  683. ob_end_flush();
  684. ob_end_clean();
  685. fclose($logHandle);
  686. }
  687. }
  688. static function _shutdownHandler()
  689. {
  690. if (!self::$enabled) {
  691. return;
  692. }
  693. static $types = array(
  694. E_ERROR => 1,
  695. E_CORE_ERROR => 1,
  696. E_COMPILE_ERROR => 1,
  697. E_PARSE => 1,
  698. );
  699. $error = error_get_last();
  700. if (isset($types[$error['type']])) {
  701. self::_exceptionHandler(new Nette\FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL));
  702. }
  703. if (self::$bar && !self::$productionMode && !self::$ajaxDetected && !self::$consoleMode
  704. && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()))
  705. ) {
  706. self::$bar->render();
  707. }
  708. }
  709. static function _exceptionHandler(\Exception $exception)
  710. {
  711. if (!headers_sent()) {
  712. header('HTTP/1.1 500 Internal Server Error');
  713. }
  714. $htmlMode = !self::$ajaxDetected && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()));
  715. try {
  716. if (self::$productionMode) {
  717. self::log($exception, self::ERROR);
  718. if (self::$consoleMode) {
  719. echo "ERROR: the server encountered an internal error and was unable to complete your request.\n";
  720. } elseif ($htmlMode) {
  721. ?>
  722. <!DOCTYPE html>
  723. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  724. <meta name=robots content=noindex><meta name=generator content="Nette Framework">
  725. <style>body{color:#333;background:white;width:500px;margin:100px auto}h1{font:bold 47px/1.5 sans-serif;margin:.6em 0}p{font:21px/1.5 Georgia,serif;margin:1.5em 0}small{font-size:70%;color:gray}</style>
  726. <title>Server Error</title>
  727. <h1>Server Error</h1>
  728. <p>We're sorry! The server encountered an internal error and was unable to complete your request. Please try again later.</p>
  729. <p><small>error 500</small></p>
  730. <?php
  731. }
  732. } else {
  733. if (self::$consoleMode) {
  734. echo "$exception\n";
  735. } elseif ($htmlMode) {
  736. self::$blueScreen->render($exception);
  737. if (self::$bar) {
  738. self::$bar->render();
  739. }
  740. } elseif (!self::fireLog($exception, self::ERROR)) {
  741. self::log($exception);
  742. }
  743. }
  744. foreach (self::$onFatalError as $handler) {
  745. call_user_func($handler, $exception);
  746. }
  747. } catch (\Exception $e) {
  748. echo "\nNette\\Debug FATAL ERROR: thrown ", get_class($e), ': ', $e->getMessage(),
  749. "\nwhile processing ", get_class($exception), ': ', $exception->getMessage(), "\n";
  750. }
  751. self::$enabled = FALSE;
  752. exit(255);
  753. }
  754. static function _errorHandler($severity, $message, $file, $line, $context)
  755. {
  756. if (self::$scream) {
  757. error_reporting(E_ALL | E_STRICT);
  758. }
  759. if (self::$lastError !== FALSE && ($severity & error_reporting()) === $severity) {
  760. self::$lastError = new \ErrorException($message, 0, $severity, $file, $line);
  761. return NULL;
  762. }
  763. if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
  764. throw new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context);
  765. } elseif (($severity & error_reporting()) !== $severity) {
  766. return FALSE;
  767. } elseif (self::$strictMode && !self::$productionMode) {
  768. self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context));
  769. }
  770. static $types = array(
  771. E_WARNING => 'Warning',
  772. E_COMPILE_WARNING => 'Warning',
  773. E_USER_WARNING => 'Warning',
  774. E_NOTICE => 'Notice',
  775. E_USER_NOTICE => 'Notice',
  776. E_STRICT => 'Strict standards',
  777. E_DEPRECATED => 'Deprecated',
  778. E_USER_DEPRECATED => 'Deprecated',
  779. );
  780. $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message";
  781. $count = & self::$errorPanel->data["$message|$file|$line"];
  782. if ($count++) {
  783. return NULL;
  784. } elseif (self::$productionMode) {
  785. self::log("$message in $file:$line", self::ERROR);
  786. return NULL;
  787. } else {
  788. $ok = self::fireLog(new \ErrorException($message, 0, $severity, $file, $line), self::WARNING);
  789. return self::$consoleMode || (!self::$bar && !$ok) ? FALSE : NULL;
  790. }
  791. return FALSE;
  792. }
  793. static function toStringException(\Exception $exception)
  794. {
  795. if (self::$enabled) {
  796. self::_exceptionHandler($exception);
  797. } else {
  798. trigger_error($exception->getMessage(), E_USER_ERROR);
  799. }
  800. }
  801. static function tryError()
  802. {
  803. if (!self::$enabled && self::$lastError === FALSE) {
  804. set_error_handler(array(__CLASS__, '_errorHandler'));
  805. }
  806. self::$lastError = NULL;
  807. }
  808. static function catchError(& $error)
  809. {
  810. if (!self::$enabled && self::$lastError !== FALSE) {
  811. restore_error_handler();
  812. }
  813. $error = self::$lastError;
  814. self::$lastError = FALSE;
  815. return (bool) $error;
  816. }
  817. static function dump($var, $return = FALSE)
  818. {
  819. if (!$return && self::$productionMode) {
  820. return $var;
  821. }
  822. $output = "<pre class=\"nette-dump\">" . Helpers::htmlDump($var) . "</pre>\n";
  823. if (!$return) {
  824. $trace = debug_backtrace();
  825. $i = !isset($trace[1]['class']) && isset($trace[1]['function']) && $trace[1]['function'] === 'dump' ? 1 : 0;
  826. if (isset($trace[$i]['file'], $trace[$i]['line']) && is_file($trace[$i]['file'])) {
  827. $lines = file($trace[$i]['file']);
  828. preg_match('#dump\((.*)\)#', $lines[$trace[$i]['line'] - 1], $m);
  829. $output = substr_replace(
  830. $output,
  831. ' title="' . htmlspecialchars((isset($m[0]) ? "$m[0] \n" : '') . "in file {$trace[$i]['file']} on line {$trace[$i]['line']}") . '"',
  832. 4, 0);
  833. if (self::$showLocation) {
  834. $output = substr_replace(
  835. $output,
  836. ' <small>in ' . Helpers::editorLink($trace[$i]['file'], $trace[$i]['line']) . ":{$trace[$i]['line']}</small>",
  837. -8, 0);
  838. }
  839. }
  840. }
  841. if (self::$consoleMode) {
  842. $output = htmlspecialchars_decode(strip_tags($output), ENT_NOQUOTES);
  843. }
  844. if ($return) {
  845. return $output;
  846. } else {
  847. echo $output;
  848. return $var;
  849. }
  850. }
  851. static function timer($name = NULL)
  852. {
  853. static $time = array();
  854. $now = microtime(TRUE);
  855. $delta = isset($time[$name]) ? $now - $time[$name] : 0;
  856. $time[$name] = $now;
  857. return $delta;
  858. }
  859. static function barDump($var, $title = NULL)
  860. {
  861. if (!self::$productionMode) {
  862. $dump = array();
  863. foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
  864. $dump[$key] = Helpers::clickableDump($val);
  865. }
  866. self::$dumpPanel->data[] = array('title' => $title, 'dump' => $dump);
  867. }
  868. return $var;
  869. }
  870. static function fireLog($message)
  871. {
  872. if (!self::$productionMode) {
  873. return self::$fireLogger->log($message);
  874. }
  875. }
  876. static function addPanel(IBarPanel $panel, $id = NULL)
  877. {
  878. self::$bar->addPanel($panel, $id);
  879. }
  880. }
  881. class Logger extends Nette\Object
  882. {
  883. const DEBUG = 'debug',
  884. INFO = 'info',
  885. WARNING = 'warning',
  886. ERROR = 'error',
  887. CRITICAL = 'critical';
  888. public static $emailSnooze = 172800;
  889. public $mailer = array(__CLASS__, 'defaultMailer');
  890. public $directory;
  891. public $email;
  892. function log($message, $priority = self::INFO)
  893. {
  894. if (!is_dir($this->directory)) {
  895. throw new Nette\DirectoryNotFoundException("Directory '$this->directory' is not found or is not directory.");
  896. }
  897. if (is_array($message)) {
  898. $message = implode(' ', $message);
  899. }
  900. $res = error_log(trim($message) . PHP_EOL, 3, $this->directory . '/' . strtolower($priority) . '.log');
  901. if (($priority === self::ERROR || $priority === self::CRITICAL) && $this->email && $this->mailer
  902. && @filemtime($this->directory . '/email-sent') + self::$emailSnooze < time()
  903. && @file_put_contents($this->directory . '/email-sent', 'sent')
  904. ) {
  905. call_user_func($this->mailer, $message, $this->email);
  906. }
  907. return $res;
  908. }
  909. private static function defaultMailer($message, $email)
  910. {
  911. $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] :
  912. (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '');
  913. $parts = str_replace(
  914. array("\r\n", "\n"),
  915. array("\n", PHP_EOL),
  916. array(
  917. 'headers' => "From: noreply@$host\nX-Mailer: Nette Framework\n",
  918. 'subject' => "PHP: An error occurred on the server $host",
  919. 'body' => "[" . @date('Y-m-d H:i:s') . "] $message",
  920. )
  921. );
  922. mail($email, $parts['subject'], $parts['body'], $parts['headers']);
  923. }
  924. }
  925. class FireLogger extends Nette\Object
  926. {
  927. const DEBUG = 'debug',
  928. INFO = 'info',
  929. WARNING = 'warning',
  930. ERROR = 'error',
  931. CRITICAL = 'critical';
  932. private static $payload = array('logs' => array());
  933. static function log($message, $priority = self::DEBUG)
  934. {
  935. if (!isset($_SERVER['HTTP_X_FIRELOGGER']) || headers_sent()) {
  936. return FALSE;
  937. }
  938. $item = array(
  939. 'name' => 'PHP',
  940. 'level' => $priority,
  941. 'order' => count(self::$payload['logs']),
  942. 'time' => str_pad(number_format((microtime(TRUE) - Debugger::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms',
  943. 'template' => '',
  944. 'message' => '',
  945. 'style' => 'background:#767ab6',
  946. );
  947. $args = func_get_args();
  948. if (isset($args[0]) && is_string($args[0])) {
  949. $item['template'] = array_shift($args);
  950. }
  951. if (isset($args[0]) && $args[0] instanceof \Exception) {
  952. $e = array_shift($args);
  953. $trace = $e->getTrace();
  954. if (isset($trace[0]['class']) && $trace[0]['class'] === 'Nette\Diagnostics\Debugger'
  955. && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler')
  956. ) {
  957. unset($trace[0]);
  958. }
  959. $file = str_replace(dirname(dirname(dirname($e->getFile()))), "\xE2\x80\xA6", $e->getFile());
  960. $item['template'] = ($e instanceof \ErrorException ? '' : get_class($e) . ': ')
  961. . $e->getMessage() . ($e->getCode() ? ' #' . $e->getCode() : '') . ' in ' . $file . ':' . $e->getLine();
  962. $item['pathname'] = $e->getFile();
  963. $item['lineno'] = $e->getLine();
  964. } else {
  965. $trace = debug_backtrace();
  966. if (isset($trace[1]['class']) && $trace[1]['class'] === 'Nette\Diagnostics\Debugger'
  967. && ($trace[1]['function'] === 'fireLog')
  968. ) {
  969. unset($trace[0]);
  970. }
  971. foreach ($trace as $frame) {
  972. if (isset($frame['file']) && is_file($frame['file'])) {
  973. $item['pathname'] = $frame['file'];
  974. $item['lineno'] = $frame['line'];
  975. break;
  976. }
  977. }
  978. }
  979. $item['exc_info'] = array('', '', array());
  980. $item['exc_frames'] = array();
  981. foreach ($trace as $frame) {
  982. $frame += array('file' => NULL, 'line' => NULL, 'class' => NULL, 'type' => NULL, 'function' => NULL, 'object' => NULL, 'args' => NULL);
  983. $item['exc_info'][2][] = array($frame['file'], $frame['line'], "$frame[class]$frame[type]$frame[function]", $frame['object']);
  984. $item['exc_frames'][] = $frame['args'];
  985. }
  986. if (isset($args[0]) && in_array($args[0], array(self::DEBUG, self::INFO, self::WARNING, self::ERROR, self::CRITICAL), TRUE)) {
  987. $item['level'] = array_shift($args);
  988. }
  989. $item['args'] = $args;
  990. self::$payload['logs'][] = self::jsonDump($item, -1);
  991. foreach (str_split(base64_encode(@json_encode(self::$payload)), 4990) as $k => $v) {
  992. header("FireLogger-de11e-$k:$v");
  993. }
  994. return TRUE;
  995. }
  996. private static function jsonDump(&$var, $level = 0)
  997. {
  998. if (is_bool($var) || is_null($var) || is_int($var) || is_float($var)) {
  999. return $var;
  1000. } elseif (is_string($var)) {
  1001. if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) {
  1002. $var = substr($var, 0, Debugger::$maxLen) . " \xE2\x80\xA6 ";
  1003. }
  1004. return Nette\Utils\Strings::fixEncoding($var);
  1005. } elseif (is_array($var)) {
  1006. static $marker;
  1007. if ($marker === NULL) {
  1008. $marker = uniqid("\x00", TRUE);
  1009. }
  1010. if (isset($var[$marker])) {
  1011. return "\xE2\x80\xA6RECURSION\xE2\x80\xA6";
  1012. } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) {
  1013. $var[$marker] = TRUE;
  1014. $res = array();
  1015. foreach ($var as $k => &$v) {
  1016. if ($k !== $marker) {
  1017. $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1);
  1018. }
  1019. }
  1020. unset($var[$marker]);
  1021. return $res;
  1022. } else {
  1023. return " \xE2\x80\xA6 ";
  1024. }
  1025. } elseif (is_object($var)) {
  1026. $arr = (array) $var;
  1027. static $list = array();
  1028. if (in_array($var, $list, TRUE)) {
  1029. return "\xE2\x80\xA6RECURSION\xE2\x80\xA6";
  1030. } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) {
  1031. $list[] = $var;
  1032. $res = array("\x00" => '(object) ' . get_class($var));
  1033. foreach ($arr as $k => &$v) {
  1034. if ($k[0] === "\x00") {
  1035. $k = substr($k, strrpos($k, "\x00") + 1);
  1036. }
  1037. $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1);
  1038. }
  1039. array_pop($list);
  1040. return $res;
  1041. } else {
  1042. return " \xE2\x80\xA6 ";
  1043. }
  1044. } elseif (is_resource($var)) {
  1045. return "resource " . get_resource_type($var);
  1046. } else {
  1047. return "unknown type";
  1048. }
  1049. }
  1050. }
  1051. class BlueScreen extends Nette\Object
  1052. {
  1053. private $panels = array();
  1054. function addPanel($panel, $id = NULL)
  1055. {
  1056. if ($id === NULL) {
  1057. $this->panels[] = $panel;
  1058. } else {
  1059. $this->panels[$id] = $panel;
  1060. }
  1061. }
  1062. function render(\Exception $exception)
  1063. {
  1064. $panels = $this->panels;
  1065. static $errorTypes = array(
  1066. E_ERROR => 'Fatal Error',
  1067. E_USER_ERROR => 'User Error',
  1068. E_RECOVERABLE_ERROR => 'Recoverable Error',
  1069. E_CORE_ERROR => 'Core Error',
  1070. E_COMPILE_ERROR => 'Compile Error',
  1071. E_PARSE => 'Parse Error',
  1072. E_WARNING => 'Warning',
  1073. E_CORE_WARNING => 'Core Warning',
  1074. E_COMPILE_WARNING => 'Compile Warning',
  1075. E_USER_WARNING => 'User Warning',
  1076. E_NOTICE => 'Notice',
  1077. E_USER_NOTICE => 'User Notice',
  1078. E_STRICT => 'Strict',
  1079. E_DEPRECATED => 'Deprecated',
  1080. E_USER_DEPRECATED => 'User Deprecated',
  1081. );
  1082. $title = ($exception instanceof Nette\FatalErrorException && isset($errorTypes[$exception->getSeverity()])) ? $errorTypes[$exception->getSeverity()] : get_class($exception);
  1083. $expandPath = NETTE_DIR . DIRECTORY_SEPARATOR;
  1084. $counter = 0;
  1085. ?><!-- "' --></script></style></pre></xmp></table>
  1086. <!DOCTYPE html>
  1087. <html>
  1088. <head>
  1089. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  1090. <meta name="robots" content="noindex,noarchive">
  1091. <meta name="generator" content="Nette Framework">
  1092. <title><?php echo htmlspecialchars($title) ?></title><!-- <?php
  1093. $ex = $exception; echo $ex->getMessage(), ($ex->getCode() ? ' #' . $ex->getCode() : '');
  1094. while ((method_exists($ex, 'getPrevious') && $ex = $ex->getPrevious()) || (isset($ex->previous) && $ex = $ex->previous)) echo '; caused by ', get_class($ex), ' ', $ex->getMessage(), ($ex->getCode() ? ' #' . $ex->getCode() : '');
  1095. ?> -->
  1096. <style type="text/css" class="nette">html{overflow-y:scroll}body{margin:0 0 2em;padding:0}#netteBluescreen{font:9pt/1.5 Verdana,sans-serif;background:white;color:#333;position:absolute;left:0;top:0;width:100%;z-index:23178;text-align:left}#netteBluescreen *{font:inherit;color:inherit;background:transparent;border:none;margin:0;padding:0;text-align:inherit;text-indent:0}#netteBluescreen b{font-weight:bold}#netteBluescreen i{font-style:italic}#netteBluescreen a{text-decoration:none;color:#328ADC;padding:2px 4px;margin:-2px -4px}#netteBluescreen a:hover,#netteBluescreen a:active,#netteBluescreen a:focus{color:#085AA3}#netteBluescreen a abbr{font-family:sans-serif;color:#BBB}#netteBluescreenIcon{position:absolute;right:.5em;top:.5em;z-index:23179;text-decoration:none;background:#CD1818;padding:3px}#netteBluescreenError{background:#CD1818;color:white;font:13pt/1.5 Verdana,sans-serif!important;display:block}#netteBluescreenError #netteBsSearch{color:#CD1818;font-size:.7em}#netteBluescreenError:hover #netteBsSearch{color:#ED8383}#netteBluescreen h1{font-size:18pt;font-weight:normal;text-shadow:1px 1px 0 rgba(0,0,0,.4);margin:.7em 0}#netteBluescreen h2{font:14pt/1.5 sans-serif!important;color:#888;margin:.6em 0}#netteBluescreen h3{font:bold 10pt/1.5 Verdana,sans-serif!important;margin:1em 0;padding:0}#netteBluescreen p,#netteBluescreen pre{margin:.8em 0}#netteBluescreen pre,#netteBluescreen code,#netteBluescreen table{font:9pt/1.5 Consolas,monospace!important}#netteBluescreen pre,#netteBluescreen table{background:#FDF5CE;padding:.4em .7em;border:1px dotted silver;overflow:auto}#netteBluescreen table pre{padding:0;margin:0;border:none}#netteBluescreen pre.nette-dump span{color:#C22}#netteBluescreen pre.nette-dump a{color:#333}#netteBluescreen div.panel{padding:1px 25px}#netteBluescreen div.inner{background:#F4F3F1;padding:.1em 1em 1em;border-radius:8px;-moz-border-radius:8px;-webkit-border-radius:8px}#netteBluescreen table{border-collapse:collapse;width:100%}#netteBluescreen .outer{overflow:auto}#netteBluescreen td,#netteBluescreen th{vertical-align:top;text-align:left;padding:2px 6px;border:1px solid #e6dfbf}#netteBluescreen th{width:10%;font-weight:bold}#netteBluescreen tr:nth-child(2n),#netteBluescreen tr:nth-child(2n) pre{background-color:#F7F0CB}#netteBluescreen ol{margin:1em 0;padding-left:2.5em}#netteBluescreen ul{font:7pt/1.5 Verdana,sans-serif!important;padding:2em 4em;margin:1em 0 0;color:#777;background:#F6F5F3 url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAAAjCAMAAADbuxbOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADBQTFRF/fz24d7Y7Onj5uLd9vPu3drUzMvG09LN39zW8e7o2NbQ3NnT29jS0M7J1tXQAAAApvmsFgAAABB0Uk5T////////////////////AOAjXRkAAAKlSURBVHja7FbbsqQgDAwENEgc//9vN+SCWDtbtXPmZR/Wc6o02mlC58LA9ckFAOszvMV8xNgyUjyXhojfMVKvRL0ZHavxXYy5JrmchMdzou8YlTClxajtK8ZGGpWRoBr1+gFjKfHkJPbizabLgzE3pH7Iu4K980xgFvlrVzMZoVBWhtvouCDdcTDmTgMCJdVxJ9MKO6XxnliM7hxi5lbj2ZVM4l8DqYyKoNLYcfqBB1/LpHYxEcfVG6ZpMDgyFUVWY/Q1sSYPpIdSAKWqLWL0XqWiMWc4hpH0OQOMOAgdycY4N9Sb7wWANQs3rsDSdLAYiuxi5siVfOhBWIrtH0G3kNaF/8Q4kCPE1kMucG/ZMUBUCOgiKJkPuWWTLGVgLGpwns1DraUayCtoBqERyaYtVsm85NActRooezvSLO/sKZP/nq8n4+xcyjNsRu8zW6KWpdb7wjiQd4WrtFZYFiKHENSmWp6xshh96c2RQ+c7Lt+qbijyEjHWUJ/pZsy8MGIUuzNiPySK2Gqoh6ZTRF6ko6q3nVTkaA//itIrDpW6l3SLo8juOmqMXkYknu5FdQxWbhCfKHEGDhxxyTVaXJF3ZjSl3jMksjSOOKmne9pI+mcG5QvaUJhI9HpkmRo2NpCrDJvsktRhRE2MM6F2n7dt4OaMUq8bCctk0+PoMRzL+1l5PZ2eyM/Owr86gf8z/tOM53lom5+nVcFuB+eJVzlXwAYy9TZ9s537tfqcsJWbEU4nBngZo6FfO9T9CdhfBtmk2dLiAy8uS4zwOpMx2HqYbTC+amNeAYTpsP4SIgvWfUBWXxn3CMHW3ffd7k3+YIkx7w0t/CVGvcPejoeOlzOWzeGbawOHqXQGUTMZRcfj4XPCgW9y/fuvVn8zD9P1QHzv80uAAQA0i3Jer7Jr7gAAAABJRU5ErkJggg==') 99% 10px no-repeat;border-top:1px solid #DDD}#netteBluescreen .highlight{background:#CD1818;color:white;font-weight:bold;font-style:normal;display:table;padding:0 .4em;margin:0 -.4em}#netteBluescreen .line{color:#9F9C7F;font-weight:normal;font-style:normal}#netteBluescreen a[href^=editor\:]{color:inherit;border-bottom:1px dotted #C1D2E1}</style>
  1097. </head>
  1098. <body>
  1099. <div id="netteBluescreen">
  1100. <a id="netteBluescreenIcon" href="#" rel="next"><abbr>&#x25bc;</abbr></a
  1101. ><div>
  1102. <div id="netteBluescreenError" class="panel">
  1103. <h1><?php echo htmlspecialchars($title), ($exception->getCode() ? ' #' . $exception->getCode() : '') ?></h1>
  1104. <p><?php echo htmlspecialchars($exception->getMessage()) ?> <a href="http://www.google.cz/search?sourceid=nette&amp;q=<?php echo urlencode($title . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $exception->getMessage())) ?>" id="netteBsSearch">search&#x25ba;</a></p>
  1105. </div>
  1106. <?php $ex = $exception; $level = 0; ?>
  1107. <?php do { ?>
  1108. <?php if ($level++): ?>
  1109. <div class="panel">
  1110. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">Caused by <abbr><?php echo ($collapsed = $level > 2) ? '&#x25ba;' : '&#x25bc;' ?></abbr></a></h2>
  1111. <div id="netteBsPnl<?php echo $counter ?>" class="<?php echo $collapsed ? 'nette-collapsed ' : '' ?>inner">
  1112. <div class="panel">
  1113. <h1><?php echo htmlspecialchars(get_class($ex)), ($ex->getCode() ? ' #' . $ex->getCode() : '') ?></h1>
  1114. <p><b><?php echo htmlspecialchars($ex->getMessage()) ?></b></p>
  1115. </div>
  1116. <?php endif ?>
  1117. <?php foreach ($panels as $panel): ?>
  1118. <?php $panel = call_user_func($panel, $ex); if (empty($panel['tab']) || empty($panel['panel'])) continue; ?>
  1119. <div class="panel">
  1120. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>"><?php echo htmlSpecialChars($panel['tab']) ?> <abbr>&#x25bc;</abbr></a></h2>
  1121. <div id="netteBsPnl<?php echo $counter ?>" class="inner">
  1122. <?php echo $panel['panel'] ?>
  1123. </div></div>
  1124. <?php endforeach ?>
  1125. <?php $stack = $ex->getTrace(); $expanded = NULL ?>
  1126. <?php if (strpos($ex->getFile(), $expandPath) === 0) {
  1127. foreach ($stack as $key => $row) {
  1128. if (isset($row['file']) && strpos($row['file'], $expandPath) !== 0) { $expanded = $key; break; }
  1129. }
  1130. } ?>
  1131. <div class="panel">
  1132. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">Source file <abbr><?php echo ($collapsed = $expanded !== NULL) ? '&#x25ba;' : '&#x25bc;' ?></abbr></a></h2>
  1133. <div id="netteBsPnl<?php echo $counter ?>" class="<?php echo $collapsed ? 'nette-collapsed ' : '' ?>inner">
  1134. <p><b>File:</b> <?php echo Helpers::editorLink($ex->getFile(), $ex->getLine()) ?> &nbsp; <b>Line:</b> <?php echo $ex->getLine() ?></p>
  1135. <?php if (is_file($ex->getFile())): ?><pre><?php echo self::highlightFile($ex->getFile(), $ex->getLine(), 15, isset($ex->context) ? $ex->context : NULL) ?></pre><?php endif ?>
  1136. </div></div>
  1137. <?php if (isset($stack[0]['class']) && $stack[0]['class'] === 'Nette\Diagnostics\Debugger' && ($stack[0]['function'] === '_shutdownHandler' || $stack[0]['function'] === '_errorHandler')) unset($stack[0]) ?>
  1138. <?php if ($stack): ?>
  1139. <div class="panel">
  1140. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">Call stack <abbr>&#x25bc;</abbr></a></h2>
  1141. <div id="netteBsPnl<?php echo $counter ?>" class="inner">
  1142. <ol>
  1143. <?php foreach ($stack as $key => $row): ?>
  1144. <li><p>
  1145. <?php if (isset($row['file']) && is_file($row['file'])): ?>
  1146. <?php echo Helpers::editorLink($row['file'], $row['line']), ':', $row['line'] ?>
  1147. <?php else: ?>
  1148. <i>inner-code</i><?php if (isset($row['line'])) echo ':', $row['line'] ?>
  1149. <?php endif ?>
  1150. <?php if (isset($row['file']) && is_file($row['file'])): ?><a href="#" rel="netteBsSrc<?php echo "$level-$key" ?>">source <abbr>&#x25ba;</abbr></a>&nbsp; <?php endif ?>
  1151. <?php if (isset($row['class'])) echo $row['class'] . $row['type'] ?>
  1152. <?php echo $row['function'] ?>
  1153. (<?php if (!empty($row['args'])): ?><a href="#" rel="netteBsArgs<?php echo "$level-$key" ?>">arguments <abbr>&#x25ba;</abbr></a><?php endif ?>)
  1154. </p>
  1155. <?php if (!empty($row['args'])): ?>
  1156. <div class="nette-collapsed outer" id="netteBsArgs<?php echo "$level-$key" ?>">
  1157. <table>
  1158. <?php
  1159. try {
  1160. $r = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']);
  1161. $params = $r->getParameters();
  1162. } catch (\Exception $e) {
  1163. $params = array();
  1164. }
  1165. foreach ($row['args'] as $k => $v) {
  1166. echo '<tr><th>', (isset($params[$k]) ? '$' . $params[$k]->name : "#$k"), '</th><td>';
  1167. echo Helpers::clickableDump($v);
  1168. echo "</td></tr>\n";
  1169. }
  1170. ?>
  1171. </table>
  1172. </div>
  1173. <?php endif ?>
  1174. <?php if (isset($row['file']) && is_file($row['file'])): ?>
  1175. <pre <?php if ($expanded !== $key) echo 'class="nette-collapsed"'; ?> id="netteBsSrc<?php echo "$level-$key" ?>"><?php echo self::highlightFile($row['file'], $row['line']) ?></pre>
  1176. <?php endif ?>
  1177. </li>
  1178. <?php endforeach ?>
  1179. </ol>
  1180. </div></div>
  1181. <?php endif ?>
  1182. <?php if (isset($ex->context) && is_array($ex->context)):?>
  1183. <div class="panel">
  1184. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">Variables <abbr>&#x25ba;</abbr></a></h2>
  1185. <div id="netteBsPnl<?php echo $counter ?>" class="nette-collapsed inner">
  1186. <div class="outer">
  1187. <table>
  1188. <?php
  1189. foreach ($ex->context as $k => $v) {
  1190. echo '<tr><th>$', htmlspecialchars($k), '</th><td>', Helpers::clickableDump($v), "</td></tr>\n";
  1191. }
  1192. ?>
  1193. </table>
  1194. </div>
  1195. </div></div>
  1196. <?php endif ?>
  1197. <?php } while ((method_exists($ex, 'getPrevious') && $ex = $ex->getPrevious()) || (isset($ex->previous) && $ex = $ex->previous)); ?>
  1198. <?php while (--$level) echo '</div></div>' ?>
  1199. <?php foreach ($panels as $panel): ?>
  1200. <?php $panel = call_user_func($panel, NULL); if (empty($panel['tab']) || empty($panel['panel'])) continue; ?>
  1201. <div class="panel">
  1202. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>"><?php echo htmlSpecialChars($panel['tab']) ?> <abbr>&#x25ba;</abbr></a></h2>
  1203. <div id="netteBsPnl<?php echo $counter ?>" class="nette-collapsed inner">
  1204. <?php echo $panel['panel'] ?>
  1205. </div></div>
  1206. <?php endforeach ?>
  1207. <div class="panel">
  1208. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">Environment <abbr>&#x25ba;</abbr></a></h2>
  1209. <div id="netteBsPnl<?php echo $counter ?>" class="nette-collapsed inner">
  1210. <?php
  1211. $list = get_defined_constants(TRUE);
  1212. if (!empty($list['user'])):?>
  1213. <h3><a href="#" rel="netteBsPnl-env-const">Constants <abbr>&#x25bc;</abbr></a></h3>
  1214. <div class="outer">
  1215. <table id="netteBsPnl-env-const">
  1216. <?php
  1217. foreach ($list['user'] as $k => $v) {
  1218. echo '<tr><th>', htmlspecialchars($k), '</th>';
  1219. echo '<td>', Helpers::clickableDump($v), "</td></tr>\n";
  1220. }
  1221. ?>
  1222. </table>
  1223. </div>
  1224. <?php endif ?>
  1225. <h3><a href="#" rel="netteBsPnl-env-files">Included files <abbr>&#x25ba;</abbr></a> (<?php echo count(get_included_files()) ?>)</h3>
  1226. <div class="outer">
  1227. <table id="netteBsPnl-env-files" class="nette-collapsed">
  1228. <?php
  1229. foreach (get_included_files() as $v) {
  1230. echo '<tr><td>', htmlspecialchars($v), "</td></tr>\n";
  1231. }
  1232. ?>
  1233. </table>
  1234. </div>
  1235. <h3>$_SERVER</h3>
  1236. <?php if (empty($_SERVER)):?>
  1237. <p><i>empty</i></p>
  1238. <?php else: ?>
  1239. <div class="outer">
  1240. <table>
  1241. <?php
  1242. foreach ($_SERVER as $k => $v) echo '<tr><th>', htmlspecialchars($k), '</th><td>', Helpers::clickableDump($v), "</td></tr>\n";
  1243. ?>
  1244. </table>
  1245. </div>
  1246. <?php endif ?>
  1247. </div></div>
  1248. <div class="panel">
  1249. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">HTTP request <abbr>&#x25ba;</abbr></a></h2>
  1250. <div id="netteBsPnl<?php echo $counter ?>" class="nette-collapsed inner">
  1251. <?php if (function_exists('apache_request_headers')): ?>
  1252. <h3>Headers</h3>
  1253. <div class="outer">
  1254. <table>
  1255. <?php
  1256. foreach (apache_request_headers() as $k => $v) echo '<tr><th>', htmlspecialchars($k), '</th><td>', htmlspecialchars($v), "</td></tr>\n";
  1257. ?>
  1258. </table>
  1259. </div>
  1260. <?php endif ?>
  1261. <?php foreach (array('_GET', '_POST', '_COOKIE') as $name): ?>
  1262. <h3>$<?php echo $name ?></h3>
  1263. <?php if (empty($GLOBALS[$name])):?>
  1264. <p><i>empty</i></p>
  1265. <?php else: ?>
  1266. <div class="outer">
  1267. <table>
  1268. <?php
  1269. foreach ($GLOBALS[$name] as $k => $v) echo '<tr><th>', htmlspecialchars($k), '</th><td>', Helpers::clickableDump($v), "</td></tr>\n";
  1270. ?>
  1271. </table>
  1272. </div>
  1273. <?php endif ?>
  1274. <?php endforeach ?>
  1275. </div></div>
  1276. <div class="panel">
  1277. <h2><a href="#" rel="netteBsPnl<?php echo ++$counter ?>">HTTP response <abbr>&#x25ba;</abbr></a></h2>
  1278. <div id="netteBsPnl<?php echo $counter ?>" class="nette-collapsed inner">
  1279. <h3>Headers</h3>
  1280. <?php if (headers_list()): ?>
  1281. <pre><?php
  1282. foreach (headers_list() as $s) echo htmlspecialchars($s), '<br>';
  1283. ?></pre>
  1284. <?php else: ?>
  1285. <p><i>no headers</i></p>
  1286. <?php endif ?>
  1287. </div></div>
  1288. <ul>
  1289. <li>Report generated at <?php echo @date('Y/m/d H:i:s', Debugger::$time) ?></li>
  1290. <?php if (preg_match('#^https?://#', Debugger::$source)): ?>
  1291. <li><a href="<?php echo htmlSpecialChars(Debugger::$source) ?>"><?php echo htmlSpecialChars(Debugger::$source) ?></a></li>
  1292. <?php elseif (Debugger::$source): ?>
  1293. <li><?php echo htmlSpecialChars(Debugger::$source) ?></li>
  1294. <?php endif ?>
  1295. <li>PHP <?php echo htmlSpecialChars(PHP_VERSION) ?></li>
  1296. <?php if (isset($_SERVER['SERVER_SOFTWARE'])): ?><li><?php echo htmlSpecialChars($_SERVER['SERVER_SOFTWARE']) ?></li><?php endif ?>
  1297. <?php if (class_exists('Nette\Framework')): ?><li><?php echo htmlSpecialChars('Nette Framework ' . Nette\Framework::VERSION) ?> <i>(revision <?php echo htmlSpecialChars(Nette\Framework::REVISION) ?>)</i></li><?php endif ?>
  1298. </ul>
  1299. </div>
  1300. </div>
  1301. <script type="text/javascript">/*<![CDATA[*/var bs=document.getElementById("netteBluescreen");document.body.appendChild(bs);document.onkeyup=function(b){b=b||window.event;b.keyCode==27&&!b.shiftKey&&!b.altKey&&!b.ctrlKey&&!b.metaKey&&bs.onclick({target:document.getElementById("netteBluescreenIcon")})};
  1302. for(var i=0,styles=document.styleSheets;i<styles.length;i++)if((styles[i].owningElement||styles[i].ownerNode).className!=="nette"){styles[i].oldDisabled=styles[i].disabled;styles[i].disabled=true}else styles[i].addRule?styles[i].addRule(".nette-collapsed","display: none"):styles[i].insertRule(".nette-collapsed { display: none }",0);
  1303. bs.onclick=function(b){b=b||window.event;for(var a=b.target||b.srcElement;a&&a.tagName&&a.tagName.toLowerCase()!=="a";)a=a.parentNode;if(!a||!a.rel)return true;for(var d=a.getElementsByTagName("abbr")[0],c=a.rel==="next"?a.nextSibling:document.getElementById(a.rel);c.nodeType!==1;)c=c.nextSibling;b=c.currentStyle?c.currentStyle.display=="none":getComputedStyle(c,null).display=="none";try{d.innerHTML=String.fromCharCode(b?9660:9658)}catch(e){}c.style.display=b?c.tagName.toLowerCase()==="code"?"inline":
  1304. "block":"none";if(a.id==="netteBluescreenIcon"){a=0;for(d=document.styleSheets;a<d.length;a++)if((d[a].owningElement||d[a].ownerNode).className!=="nette")d[a].disabled=b?true:d[a].oldDisabled}return false};/*]]>*/</script>
  1305. </body>
  1306. </html>
  1307. <?php
  1308. }
  1309. static function highlightFile($file, $line, $count = 15, $vars = array())
  1310. {
  1311. if (function_exists('ini_set')) {
  1312. ini_set('highlight.comment', '#999; font-style: italic');
  1313. ini_set('highlight.default', '#000');
  1314. ini_set('highlight.html', '#06B');
  1315. ini_set('highlight.keyword', '#D24; font-weight: bold');
  1316. ini_set('highlight.string', '#080');
  1317. }
  1318. $start = max(1, $line - floor($count / 2));
  1319. $source = @file_get_contents($file);
  1320. if (!$source) {
  1321. return;
  1322. }
  1323. $source = explode("\n", highlight_string($source, TRUE));
  1324. $spans = 1;
  1325. $out = $source[0];
  1326. $source = explode('<br />', $source[1]);
  1327. array_unshift($source, NULL);
  1328. $i = $start;
  1329. while (--$i >= 1) {
  1330. if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) {
  1331. if ($m[1] !== '</span>') {
  1332. $spans++; $out .= $m[1];
  1333. }
  1334. break;
  1335. }
  1336. }
  1337. $source = array_slice($source, $start, $count, TRUE);
  1338. end($source);
  1339. $numWidth = strlen((string) key($source));
  1340. foreach ($source as $n => $s) {
  1341. $spans += substr_count($s, '<span') - substr_count($s, '</span');
  1342. $s = str_replace(array("\r", "\n"), array('', ''), $s);
  1343. preg_match_all('#<[^>]+>#', $s, $tags);
  1344. if ($n === $line) {
  1345. $out .= sprintf(
  1346. "<span class='highlight'>%{$numWidth}s: %s\n</span>%s",
  1347. $n,
  1348. strip_tags($s),
  1349. implode('', $tags[0])
  1350. );
  1351. } else {
  1352. $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s);
  1353. }
  1354. }
  1355. $out .= str_repeat('</span>', $spans) . '</code>';
  1356. $out = preg_replace_callback('#">\$(\w+)(&nbsp;)?</span>#', function($m) use($vars) {
  1357. return isset($vars[$m[1]])
  1358. ? '" title="' . str_replace('"', '&quot;', strip_tags(Helpers::htmlDump($vars[$m[1]]))) . $m[0]
  1359. : $m[0];
  1360. }, $out);
  1361. return $out;
  1362. }
  1363. }
  1364. class Bar extends Nette\Object
  1365. {
  1366. private $panels = array();
  1367. function addPanel(IBarPanel $panel, $id = NULL)
  1368. {
  1369. if ($id === NULL) {
  1370. $c = 0;
  1371. do {
  1372. $id = get_class($panel) . ($c++ ? "-$c" : '');
  1373. } while (isset($this->panels[$id]));
  1374. }
  1375. $this->panels[$id] = $panel;
  1376. }
  1377. function render()
  1378. {
  1379. $panels = array();
  1380. foreach ($this->panels as $id => $panel) {
  1381. try {
  1382. $panels[] = array(
  1383. 'id' => preg_replace('#[^a-z0-9]+#i', '-', $id),
  1384. 'tab' => $tab = (string) $panel->getTab(),
  1385. 'panel' => $tab ? (string) $panel->getPanel() : NULL,
  1386. );
  1387. } catch (\Exception $e) {
  1388. $panels[] = array(
  1389. 'id' => "error-$id",
  1390. 'tab' => "Error: $id",
  1391. 'panel' => nl2br(htmlSpecialChars((string) $e)),
  1392. );
  1393. }
  1394. }
  1395. ?>
  1396. <!-- Nette Debug Bar -->
  1397. <?php ob_start() ?>
  1398. &nbsp;
  1399. <style id="nette-debug-style" class="nette">#nette-debug{display:none;position:fixed}body#nette-debug{margin:5px 5px 0;display:block}#nette-debug *{font:inherit;color:inherit;background:transparent;margin:0;padding:0;border:none;text-align:inherit;list-style:inherit}#nette-debug .nette-fixed-coords{position:fixed;_position:absolute;right:0;bottom:0;max-width:100%}#nette-debug a{color:#125EAE;text-decoration:none}#nette-debug .nette-panel a{color:#125EAE;text-decoration:none}#nette-debug a:hover,#nette-debug a:active,#nette-debug a:focus{background-color:#125EAE;color:white}#nette-debug .nette-panel h2,#nette-debug .nette-panel h3,#nette-debug .nette-panel p{margin:.4em 0}#nette-debug .nette-panel table{border-collapse:collapse;background:#FDF5CE}#nette-debug .nette-panel tr:nth-child(2n) td{background:#F7F0CB}#nette-debug .nette-panel td,#nette-debug .nette-panel th{border:1px solid #E6DFBF;padding:2px 5px;vertical-align:top;text-align:left}#nette-debug .nette-panel th{background:#F4F3F1;color:#655E5E;font-size:90%;font-weight:bold}#nette-debug .nette-panel pre,#nette-debug .nette-panel code{font:9pt/1.5 Consolas,monospace}#nette-debug table .nette-right{text-align:right}.nette-hidden,.nette-collapsed{display:none}#nette-debug-bar{font:normal normal 12px/21px Tahoma,sans-serif;color:#333;border:1px solid #c9c9c9;background:#EDEAE0 url('data:image/png;base64,R0lGODlhAQAVALMAAOTh1/Px6eHe1fHv5e/s4vLw6Ofk2u3q4PPw6PPx6PDt5PLw5+Dd1OXi2Ojm3Orn3iH5BAAAAAAALAAAAAABABUAAAQPMISEyhpYkfOcaQAgCEwEADs=') top;position:relative;overflow:auto;min-height:21px;_float:left;min-width:50px;white-space:nowrap;z-index:23181;opacity:.9;border-radius:3px;-moz-border-radius:3px;box-shadow:1px 1px 10px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 10px rgba(0,0,0,.15);-webkit-box-shadow:1px 1px 10px rgba(0,0,0,.15)}#nette-debug-bar:hover{opacity:1}#nette-debug-bar ul{list-style:none none;margin-left:4px}#nette-debug-bar li{float:left}#nette-debug-bar img{vertical-align:middle;position:relative;top:-1px;margin-right:3px}#nette-debug-bar li a{color:#000;display:block;padding:0 4px}#nette-debug-bar li a:hover{color:black;background:#c3c1b8}#nette-debug-bar li .nette-warning{color:#D32B2B;font-weight:bold}#nette-debug-bar li>span{padding:0 4px}#nette-debug-logo{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAPCAYAAABwfkanAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABiFJREFUSMe1VglPlGcQ5i+1xjZNqxREtGq8ahCPWsVGvEDBA1BBRQFBDjkE5BYUzwpovRBUREBEBbl3OVaWPfj2vi82eTrvbFHamLRJ4yYTvm+u95mZZ96PoKAv+LOatXBYZ+Bx6uFy6DGnt1m0EOKwSmQzwmHTgX5B/1W+yM9GYJ02CX6/B/5ZF+w2A4x6FYGTYDVp4PdY2Tbrs5N+mnRa2Km4/wV6rhPzQQj5fDc1mJM5nd0iYdZtQWtrCxobGnDpUiledTynbuvg99mgUMhw924Trl2rR01NNSTNJE9iDpTV8innv4K2kZPLroPXbYLHZeSu2K1aeF0muJ2GvwGzmNSwU2E+svm8ZrgdBliMaha/34Vx+RAKCgpwpa4OdbW1UE/L2cc/68WtWzdRVlaG6uoqtD1/BA/pA1MIxLvtes7pc5vhoDOE/rOgbVSdf9aJWa8dBp0Kyg+jdLiTx2vQKWEyqGmcNkqg4iTC1+dzQatWkK+cJqPD7KyFaKEjvRuNjY24fLkGdXW1ePjwAeX4QHonDNI0A75+/RpqqqshH+6F2UAUMaupYXouykV0mp6SQ60coxgL8Z4aMg/4x675/V60v3jKB+Xl5WJibIC4KPEIS0qKqWv5GOh7BZ/HSIk9kA33o7y8DOfPZ6GQOipkXDZAHXKxr4ipqqpkKS6+iIrycgz2dyMnJxtVlZUsotNZWZmor79KBbvgpdjm5sfIzc1hv4L8fKJPDTfJZZc+gRYKr8sAEy2DcBRdEEk62ltx9uwZ5qNILoDU1l6mbrvx5EkzUlKSuTiR7PHjR3x4fv4FyIbeIic7G5WVFUyN+qtX+Lnt2SPcvn2LfURjhF7kE4WPDr+Bx+NEUVEhkpNPoImm5CSOl5aUIC3tLOMR59gtAY4HidGIzj14cB8ZGRkM8kJeHk6cOI4xWR8vSl5uLlJTT6O74xnT5lB8PM6cSYXVqILb5UBWZiYSExMYkE4zzjqX00QHG+h9AjPqMei0k3ywy2khMdNiq6BVCf04T6ekuBgJCUdRUVHOBQwPvkNSUiLjaGi4Q/5qFgYtHgTXRJdTT59GenoaA5gY64deq0Bc3EGuNj4+DnppEheLijhZRkY6SktLsGPHdi6irOwSFTRAgO04deokTSIFsbExuHfvLnFSx8DevelAfFwcA0lJTqZi5PDS9aci/sbE7Oe4wsICbtD27b/ye1NTI3FeSX4W2gdFALRD3A4eM44ePcKViuD79/8gnZP5Kg4+cCAW2dnnqUM2Lujw4UM4ePAA2ztfPsHIYA/sdOt43A50d7UFCjkUj+joXVBMDJDeDhcVk08cjd61C3v37uFYp8PKXX3X8xJRUTtw7FgSn3Xzxg10d7ZCqRjkM+02C7pettDNogqAFjzxuI3YHR2Nffv2coXy0V44HGZERm7kJNu2/cK8bW9rwbp1axnMnj27uUijQQOb1QyTcYZ3YMOGn/Hbzp1crAAvaDfY38O5hW3//n0ce+TIYWiUcub1xo0R2Lp1y8cYsUMWM125VhPe93Zj7do1vEPi26GfUdBFbhK8tGHrli1YsWwpgoOD0dXRQqAtXMCy8DBs3rwJoSGLsWrVclylBdoUGYlVK1dg9eqVCFsSSs8/4btvvmUwEnE0KTERISE/IiIiAsGLF2HhwgU8qbc97QgPX8qFr1mzGgu+/opzdL5o5l1aEhqC9evXYWlYKFYsD6e/YVj0w/dMGZVyBDMqeaDTRuKpkxYjIz2dOyeup6H3r2kkOuJ1H3N5Z1QUzp3LQF9vJ4xGLQYHXiM9LY0pEhsTg+PHj9HNcJu4OcL3uaQZY86LiZw8mcJTkmhBTUYJbU8fcoygobgWR4Z6iKtTPLE7d35HYkICT1dIZuY59HQ9412StBPQTMvw8Z6WaMNFxy3Gab4TeQT0M9IHwUT/G0i0MGIJ9CTiJjBIH+iQaQbC7+QnfEXiQL6xgF09TjETHCt8RbeMuil+D8RNsV1LHdQoZfR/iJJzCZuYmEE/Bd3MJNs/+0UURgFWJJ//aQ8k+CsxVTqnVytHObkQrUoG8T4/bs4u4ubbxLPwFzYNPc8HI2zijLm84l39Dx8hfwJenFezFBKKQwAAAABJRU5ErkJggg==') 0 50% no-repeat;min-width:45px;cursor:move}#nette-debug-logo span{display:none}#nette-debug-bar-bgl,#nette-debug-bar-bgx,#nette-debug-bar-bgr{position:absolute;z-index:-1;top:-7px;height:37px}#nette-debug .nette-panel{font:normal normal 12px/1.5 sans-serif;background:white;color:#333}#nette-debug h1{font:normal normal 23px/1.4 Tahoma,sans-serif;color:#575753;background:#EDEAE0;margin:-5px -5px 5px;padding:0 25px 5px 5px}#nette-debug .nette-mode-peek .nette-inner,#nette-debug .nette-mode-float .nette-inner{max-width:700px;max-height:500px;overflow:auto}#nette-debug .nette-panel .nette-icons{display:none}#nette-debug .nette-mode-peek{display:none;position:relative;z-index:23180;padding:5px;min-width:150px;min-height:50px;border:5px solid #EDEAE0;border-radius:5px;-moz-border-radius:5px}#nette-debug .nette-mode-peek h1{cursor:move}#nette-debug .nette-mode-float{position:relative;z-index:23179;padding:5px;min-width:150px;min-height:50px;border:5px solid #EDEAE0;border-radius:5px;-moz-border-radius:5px;opacity:.9;box-shadow:1px 1px 6px #666;-moz-box-shadow:1px 1px 6px rgba(0,0,0,.45);-webkit-box-shadow:1px 1px 6px #666}#nette-debug .nette-focused{z-index:23180;opacity:1}#nette-debug .nette-mode-float h1{cursor:move}#nette-debug .nette-mode-float .nette-icons{display:block;position:absolute;top:0;right:0;font-size:18px}#nette-debug .nette-icons a{color:#575753}#nette-debug .nette-icons a:hover{color:white}</style>
  1400. <!--[if lt IE 8]><style class="nette">#nette-debug-bar img{display:none}#nette-debug-bar li{border-left:1px solid #DCD7C8;padding:0 3px}#nette-debug-logo span{background:#edeae0;display:inline}</style><![endif]-->
  1401. <script id="nette-debug-script">/*<![CDATA[*/var Nette=Nette||{};
  1402. (function(){Nette.Class=function(a){var b=a.constructor||function(){},c,d=Object.prototype.hasOwnProperty;delete a.constructor;if(a.Extends){var f=function(){this.constructor=b};f.prototype=a.Extends.prototype;b.prototype=new f;delete a.Extends}if(a.Static){for(c in a.Static)if(d.call(a.Static,c))b[c]=a.Static[c];delete a.Static}for(c in a)if(d.call(a,c))b.prototype[c]=a[c];return b};Nette.Q=Nette.Class({Static:{factory:function(a){return new Nette.Q(a)},implement:function(a){var b,c=Nette.Q.implement,
  1403. d=Nette.Q.prototype,f=Object.prototype.hasOwnProperty;for(b in a)if(f.call(a,b)){c[b]=a[b];d[b]=function(i){return function(){return this.each(c[i],arguments)}}(b)}}},constructor:function(a){if(typeof a==="string")a=this._find(document,a);else if(!a||a.nodeType||a.length===undefined||a===window)a=[a];for(var b=0,c=a.length;b<c;b++)if(a[b])this[this.length++]=a[b]},length:0,find:function(a){return new Nette.Q(this._find(this[0],a))},_find:function(a,b){if(!a||!b)return[];else if(document.querySelectorAll)return a.querySelectorAll(b);
  1404. else if(b.charAt(0)==="#")return[document.getElementById(b.substring(1))];else{b=b.split(".");var c=a.getElementsByTagName(b[0]||"*");if(b[1]){for(var d=[],f=RegExp("(^|\\s)"+b[1]+"(\\s|$)"),i=0,k=c.length;i<k;i++)f.test(c[i].className)&&d.push(c[i]);return d}else return c}},dom:function(){return this[0]},each:function(a,b){for(var c=0,d;c<this.length;c++)if((d=a.apply(this[c],b||[]))!==undefined)return d;return this}});var h=Nette.Q.factory,e=Nette.Q.implement;e({bind:function(a,b){if(document.addEventListener&&
  1405. (a==="mouseenter"||a==="mouseleave")){var c=b;a=a==="mouseenter"?"mouseover":"mouseout";b=function(g){for(var j=g.relatedTarget;j;j=j.parentNode)if(j===this)return;c.call(this,g)}}var d=e.data.call(this);d=d.events=d.events||{};if(!d[a]){var f=this,i=d[a]=[],k=e.bind.genericHandler=function(g){if(!g.target)g.target=g.srcElement;if(!g.preventDefault)g.preventDefault=function(){g.returnValue=false};if(!g.stopPropagation)g.stopPropagation=function(){g.cancelBubble=true};g.stopImmediatePropagation=function(){this.stopPropagation();
  1406. j=i.length};for(var j=0;j<i.length;j++)i[j].call(f,g)};if(document.addEventListener)this.addEventListener(a,k,false);else document.attachEvent&&this.attachEvent("on"+a,k)}d[a].push(b)},addClass:function(a){this.className=this.className.replace(/^|\s+|$/g," ").replace(" "+a+" "," ")+" "+a},removeClass:function(a){this.className=this.className.replace(/^|\s+|$/g," ").replace(" "+a+" "," ")},hasClass:function(a){return this.className.replace(/^|\s+|$/g," ").indexOf(" "+a+" ")>-1},show:function(){var a=
  1407. e.show.display=e.show.display||{},b=this.tagName;if(!a[b]){var c=document.body.appendChild(document.createElement(b));a[b]=e.css.call(c,"display")}this.style.display=a[b]},hide:function(){this.style.display="none"},css:function(a){return this.currentStyle?this.currentStyle[a]:window.getComputedStyle?document.defaultView.getComputedStyle(this,null).getPropertyValue(a):undefined},data:function(){return this.nette?this.nette:this.nette={}},val:function(){var a;if(!this.nodeName){a=0;for(len=this.length;a<
  1408. len;a++)if(this[a].checked)return this[a].value;return null}if(this.nodeName.toLowerCase()==="select"){a=this.selectedIndex;var b=this.options;if(a<0)return null;else if(this.type==="select-one")return b[a].value;a=0;values=[];for(len=b.length;a<len;a++)b[a].selected&&values.push(b[a].value);return values}if(this.type==="checkbox")return this.checked;return this.value.replace(/^\s+|\s+$/g,"")},_trav:function(a,b,c){for(b=b.split(".");a&&!(a.nodeType===1&&(!b[0]||a.tagName.toLowerCase()===b[0])&&(!b[1]||
  1409. e.hasClass.call(a,b[1])));)a=a[c];return h(a)},closest:function(a){return e._trav(this,a,"parentNode")},prev:function(a){return e._trav(this.previousSibling,a,"previousSibling")},next:function(a){return e._trav(this.nextSibling,a,"nextSibling")},offset:function(a){for(var b=this,c=a?{left:-a.left||0,top:-a.top||0}:e.position.call(b);b=b.offsetParent;){c.left+=b.offsetLeft;c.top+=b.offsetTop}if(a)e.position.call(this,{left:-c.left,top:-c.top});else return c},position:function(a){if(a){this.nette&&
  1410. this.nette.onmove&&this.nette.onmove.call(this,a);this.style.left=(a.left||0)+"px";this.style.top=(a.top||0)+"px"}else return{left:this.offsetLeft,top:this.offsetTop,width:this.offsetWidth,height:this.offsetHeight}},draggable:function(a){var b=h(this),c=document.documentElement,d;a=a||{};h(a.handle||this).bind("mousedown",function(f){f.preventDefault();f.stopPropagation();if(e.draggable.binded)return c.onmouseup(f);var i=b[0].offsetLeft-f.clientX,k=b[0].offsetTop-f.clientY;e.draggable.binded=true;
  1411. d=false;c.onmousemove=function(g){g=g||event;if(!d){a.draggedClass&&b.addClass(a.draggedClass);a.start&&a.start(g,b);d=true}b.position({left:g.clientX+i,top:g.clientY+k});return false};c.onmouseup=function(g){if(d){a.draggedClass&&b.removeClass(a.draggedClass);if(a.stop)a.stop(g||event,b)}e.draggable.binded=c.onmousemove=c.onmouseup=null;return false}}).bind("click",function(f){if(d){f.stopImmediatePropagation();preventClick=false}})}})})();
  1412. (function(){Nette.Debug={};var h=Nette.Q.factory,e=Nette.Debug.Panel=Nette.Class({Extends:Nette.Q,Static:{PEEK:"nette-mode-peek",FLOAT:"nette-mode-float",WINDOW:"nette-mode-window",FOCUSED:"nette-focused",factory:function(a){return new e(a)},_toggle:function(a){var b=a.rel;b=b.charAt(0)==="#"?h(b):h(a)[b==="prev"?"prev":"next"](b.substring(4));if(b.css("display")==="none"){b.show();a.innerHTML=a.innerHTML.replace("â–º","â–¼")}else{b.hide();a.innerHTML=a.innerHTML.replace("â–¼","â–º")}}},constructor:function(a){Nette.Q.call(this,
  1413. "#nette-debug-panel-"+a.replace("nette-debug-panel-",""))},reposition:function(){if(this.hasClass(e.WINDOW))window.resizeBy(document.documentElement.scrollWidth-document.documentElement.clientWidth,document.documentElement.scrollHeight-document.documentElement.clientHeight);else{this.position(this.position());if(this.position().width)document.cookie=this.dom().id+"="+this.position().left+":"+this.position().top+"; path=/"}},focus:function(){if(this.hasClass(e.WINDOW))this.data().win.focus();else{clearTimeout(this.data().blurTimeout);
  1414. this.addClass(e.FOCUSED).show()}},blur:function(){this.removeClass(e.FOCUSED);if(this.hasClass(e.PEEK)){var a=this;this.data().blurTimeout=setTimeout(function(){a.hide()},50)}},toFloat:function(){this.removeClass(e.WINDOW).removeClass(e.PEEK).addClass(e.FLOAT).show().reposition();return this},toPeek:function(){this.removeClass(e.WINDOW).removeClass(e.FLOAT).addClass(e.PEEK).hide();document.cookie=this.dom().id+"=; path=/"},toWindow:function(){var a=this,b,c;c=this.offset();var d=this.dom().id;c.left+=
  1415. typeof window.screenLeft==="number"?window.screenLeft:window.screenX+10;c.top+=typeof window.screenTop==="number"?window.screenTop:window.screenY+50;if(b=window.open("",d.replace(/-/g,"_"),"left="+c.left+",top="+c.top+",width="+c.width+",height="+(c.height+15)+",resizable=yes,scrollbars=yes")){c=b.document;c.write('<!DOCTYPE html><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><style>'+h("#nette-debug-style").dom().innerHTML+"</style><script>"+h("#nette-debug-script").dom().innerHTML+
  1416. '<\/script><body id="nette-debug">');c.body.innerHTML='<div class="nette-panel nette-mode-window" id="'+d+'">'+this.dom().innerHTML+"</div>";b.Nette.Debug.Panel.factory(d).initToggler().reposition();c.title=a.find("h1").dom().innerHTML;h([b]).bind("unload",function(){a.toPeek();b.close()});h(c).bind("keyup",function(f){f.keyCode===27&&!f.shiftKey&&!f.altKey&&!f.ctrlKey&&!f.metaKey&&b.close()});document.cookie=d+"=window; path=/";this.hide().removeClass(e.FLOAT).removeClass(e.PEEK).addClass(e.WINDOW).data().win=
  1417. b}},init:function(){var a=this,b;a.data().onmove=function(c){var d=document,f=window.innerHeight||d.documentElement.clientHeight||d.body.clientHeight;c.left=Math.max(Math.min(c.left,0.8*this.offsetWidth),0.2*this.offsetWidth-(window.innerWidth||d.documentElement.clientWidth||d.body.clientWidth));c.top=Math.max(Math.min(c.top,0.8*this.offsetHeight),this.offsetHeight-f)};h(window).bind("resize",function(){a.reposition()});a.draggable({handle:a.find("h1"),stop:function(){a.toFloat()}}).bind("mouseenter",
  1418. function(){a.focus()}).bind("mouseleave",function(){a.blur()});this.initToggler();a.find(".nette-icons").find("a").bind("click",function(c){this.rel==="close"?a.toPeek():a.toWindow();c.preventDefault()});if(b=document.cookie.match(RegExp(a.dom().id+"=(window|(-?[0-9]+):(-?[0-9]+))")))b[2]?a.toFloat().position({left:b[2],top:b[3]}):a.toWindow();else a.addClass(e.PEEK)},initToggler:function(){var a=this;this.bind("click",function(b){var c=h(b.target).closest("a").dom();if(c&&c.rel){e._toggle(c);b.preventDefault();
  1419. a.reposition()}});return this}});Nette.Debug.Bar=Nette.Class({Extends:Nette.Q,constructor:function(){Nette.Q.call(this,"#nette-debug-bar")},init:function(){var a=this,b;a.data().onmove=function(c){var d=document,f=window.innerHeight||d.documentElement.clientHeight||d.body.clientHeight;c.left=Math.max(Math.min(c.left,0),this.offsetWidth-(window.innerWidth||d.documentElement.clientWidth||d.body.clientWidth));c.top=Math.max(Math.min(c.top,0),this.offsetHeight-f)};h(window).bind("resize",function(){a.position(a.position())});
  1420. a.draggable({draggedClass:"nette-dragged",stop:function(){document.cookie=a.dom().id+"="+a.position().left+":"+a.position().top+"; path=/"}});a.find("a").bind("click",function(c){if(this.rel==="close"){h("#nette-debug").hide();window.opera&&h("body").show()}else if(this.rel){var d=e.factory(this.rel);if(c.shiftKey)d.toFloat().toWindow();else if(d.hasClass(e.FLOAT)){var f=h(this).offset();d.offset({left:f.left-d.position().width+f.width+4,top:f.top-d.position().height-4}).toPeek()}else d.toFloat().position({left:d.position().left-
  1421. Math.round(Math.random()*100)-20,top:d.position().top-Math.round(Math.random()*100)-20}).reposition()}c.preventDefault()}).bind("mouseenter",function(){if(!(!this.rel||this.rel==="close"||a.hasClass("nette-dragged"))){var c=e.factory(this.rel);c.focus();if(c.hasClass(e.PEEK)){var d=h(this).offset();c.offset({left:d.left-c.position().width+d.width+4,top:d.top-c.position().height-4})}}}).bind("mouseleave",function(){!this.rel||this.rel==="close"||a.hasClass("nette-dragged")||e.factory(this.rel).blur()});
  1422. if(b=document.cookie.match(RegExp(a.dom().id+"=(-?[0-9]+):(-?[0-9]+)")))a.position({left:b[1],top:b[2]});a.find("a").each(function(){!this.rel||this.rel==="close"||e.factory(this.rel).init()})}})})();/*]]>*/</script>
  1423. <?php foreach ($panels as $id => $panel): if (!$panel['panel']) continue; ?>
  1424. <div class="nette-fixed-coords">
  1425. <div class="nette-panel" id="nette-debug-panel-<?php echo $panel['id'] ?>">
  1426. <?php echo $panel['panel'] ?>
  1427. <div class="nette-icons">
  1428. <a href="#" title="open in window">&curren;</a>
  1429. <a href="#" rel="close" title="close window">&times;</a>
  1430. </div>
  1431. </div>
  1432. </div>
  1433. <?php endforeach ?>
  1434. <div class="nette-fixed-coords">
  1435. <div id="nette-debug-bar">
  1436. <ul>
  1437. <li id="nette-debug-logo" title="PHP <?php echo htmlSpecialChars(PHP_VERSION . " |\n"
  1438. . (isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] . " |\n" : '')
  1439. . (class_exists('Nette\Framework') ? 'Nette Framework ' . Nette\Framework::VERSION . ' (' . substr(Nette\Framework::REVISION, 8) . ')' : '')) ?>">&nbsp;<span>Nette Framework</span></li>
  1440. <?php foreach ($panels as $panel): if (!$panel['tab']) continue; ?>
  1441. <li><?php if ($panel['panel']): ?><a href="#" rel="<?php echo $panel['id'] ?>"><?php echo trim($panel['tab']) ?></a><?php else: echo '<span>', trim($panel['tab']), '</span>'; endif ?></li>
  1442. <?php endforeach ?>
  1443. <li><a href="#" rel="close" title="close debug bar">&times;</a></li>
  1444. </ul>
  1445. </div>
  1446. </div>
  1447. <?php $output = ob_get_clean(); ?>
  1448. <div id="nette-debug"></div>
  1449. <script>
  1450. (function (onloadOrig) {
  1451. window.onload = function() {
  1452. if (typeof onloadOrig === 'function') onloadOrig();
  1453. var debug = document.getElementById('nette-debug');
  1454. document.body.appendChild(debug);
  1455. debug.innerHTML = <?php echo json_encode(Nette\Utils\Strings::fixEncoding($output)) ?>;
  1456. for (var i = 0, scripts = debug.getElementsByTagName('script'); i < scripts.length; i++) eval(scripts[i].innerHTML);
  1457. (new Nette.Debug.Bar).init();
  1458. Nette.Q.factory(debug).show();
  1459. };
  1460. })(window.onload);
  1461. </script>
  1462. <!-- /Nette Debug Bar -->
  1463. <?php
  1464. }
  1465. }
  1466. final class DefaultBarPanel extends Nette\Object implements IBarPanel
  1467. {
  1468. private $id;
  1469. public $data;
  1470. function __construct($id)
  1471. {
  1472. $this->id = $id;
  1473. }
  1474. function getTab()
  1475. {
  1476. ob_start();
  1477. $data = $this->data;
  1478. if ($this->id === 'time') {
  1479. ?>
  1480. <span title="Execution time"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ6SURBVDjLjZO7T1NhGMY7Mji6uJgYt8bElTjof6CDg4sMSqIxJsRGB5F4TwQSIg1QKC0KWmkZEEsKtEcSxF5ohV5pKSicXqX3aqGn957z+PUEGopiGJ583/A+v3znvPkJAAjWR0VNJG0kGhKahCFhXcN3YBFfx8Kry6ym4xIzce88/fbWGY2k5WRb77UTTbWuYA9gDGg7EVmSIOF4g5T7HZKuMcSW5djWDyL0uRf0dCc8inYYxTcw9fAiCMBYB3gVj1z7gLhNTjKCqHkYP79KENC9Bq3uxrrqORzy+9D3tPAAccspVx1gWg0KbaZFbGllWFM+xrKkFQudV0CeDfJsjN4+C2nracjunoPq5VXIBrowMK4V1gG1LGyWdbZwCalsBYUyh2KFQzpXxVqkAGswD3+qBDpZwow9iYE5v26/VwfUQnnznyhvjguQYabIIpKpYD1ahI8UTT92MUSFuP5Z/9TBTgOgFrVjp3nakaG/0VmEfpX58pwzjUEquNk362s+PP8XYD/KpYTBHmRg9Wch0QX1R80dCZhYipudYQY2Auib8RmODVCa4hfUK4ngaiiLNFNFdKeCWWscXZMbWy9Unv9/gsIQU09a4pwvUeA3Uapy2C2wCKXL0DqTePLexbWPOv79E8f0UWrencZ2poxciUWZlKssB4bcHeE83NsFuMgpo2iIpMuNa1TNu4XjhggWvb+R2K3wZdLlAZl8Fd9jRb5sD+Xx0RJBx5gdom6VsMEFDyWF0WyCeSOFcDKPnRxZYTQL5Rc/nn1w4oFsBaIhC3r6FRh5erPRhYMyHdeFw4C6zkRhmijM7CnMu0AUZonCDCnRJBqSus5/ABD6Ba5CkQS8AAAAAElFTkSuQmCC"
  1481. /><?php echo number_format((microtime(TRUE) - Debugger::$time) * 1000, 1, '.', ' ') ?> ms</span>
  1482. <?php
  1483. } elseif ($this->id === 'memory') {
  1484. ?>
  1485. <span title="The peak of allocated memory"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGvSURBVDjLpZO7alZREEbXiSdqJJDKYJNCkPBXYq12prHwBezSCpaidnY+graCYO0DpLRTQcR3EFLl8p+9525xgkRIJJApB2bN+gZmqCouU+NZzVef9isyUYeIRD0RTz482xouBBBNHi5u4JlkgUfx+evhxQ2aJRrJ/oFjUWysXeG45cUBy+aoJ90Sj0LGFY6anw2o1y/mK2ZS5pQ50+2XiBbdCvPk+mpw2OM/Bo92IJMhgiGCox+JeNEksIC11eLwvAhlzuAO37+BG9y9x3FTuiWTzhH61QFvdg5AdAZIB3Mw50AKsaRJYlGsX0tymTzf2y1TR9WwbogYY3ZhxR26gBmocrxMuhZNE435FtmSx1tP8QgiHEvj45d3jNlONouAKrjjzWaDv4CkmmNu/Pz9CzVh++Yd2rIz5tTnwdZmAzNymXT9F5AtMFeaTogJYkJfdsaaGpyO4E62pJ0yUCtKQFxo0hAT1JU2CWNOJ5vvP4AIcKeao17c2ljFE8SKEkVdWWxu42GYK9KE4c3O20pzSpyyoCx4v/6ECkCTCqccKorNxR5uSXgQnmQkw2Xf+Q+0iqQ9Ap64TwAAAABJRU5ErkJggg=="
  1486. /><?php echo function_exists('memory_get_peak_usage') ? number_format(memory_get_peak_usage() / 1000000, 2, '.', ' ') : 'n/a'; ?> MB</span>
  1487. <?php
  1488. } elseif ($this->id === 'dumps' && $this->data) {
  1489. ?>
  1490. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIASURBVDjLpVPPaxNREJ6Vt01caH4oWk1T0ZKlGIo9RG+BUsEK4kEP/Q8qPXnpqRdPBf8A8Wahhx7FQ0GF9FJ6UksqwfTSBDGyB5HkkphC9tfb7jfbtyQQTx142byZ75v5ZnZWC4KALmICPy+2DkvKIX2f/POz83LxCL7nrz+WPNcll49DrhM9v7xdO9JW330DuXrrqkFSgig5iR2Cfv3t3gNxOnv5BwU+eZ5HuON5/PMPJZKJ+yKQfpW0S7TxdC6WJaWkyvff1LDaFRAeLZj05MHsiPTS6hua0PUqtwC5sHq9zv9RYWl+nu5cETcnJ1M0M5WlWq3GsX6/T+VymRzHDluZiGYAAsw0TQahV8uyyGq1qFgskm0bHIO/1+sx1rFtchJhArwEyIQ1Gg2WD2A6nWawHQJVDIWgIJfLhQowTIeE9D0mKAU8qPC0220afsWFQoH93W6X7yCDJ+DEBeBmsxnPIJVKxWQVUwry+XyUwBlKMKwA8jqdDhOVCqVAzQDVvXAXhOdGBFgymYwrGoZBmUyGjxCCdF0fSahaFdgoTHRxfTveMCXvWfkuE3Y+f40qhgT/nMitupzApdvT18bu+YeDQwY9Xl4aG9/d/URiMBhQq/dvZMeVghtT17lSZW9/rAKsvPa/r9Fc2dw+Pe0/xI6kM9mT5vtXy+Nw2kU/5zOGRpvuMIu0YAAAAABJRU5ErkJggg==" />variables
  1491. <?php
  1492. } elseif ($this->id === 'errors' && $this->data) {
  1493. ?>
  1494. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIsSURBVDjLpVNLSJQBEP7+h6uu62vLVAJDW1KQTMrINQ1vPQzq1GOpa9EppGOHLh0kCEKL7JBEhVCHihAsESyJiE4FWShGRmauu7KYiv6Pma+DGoFrBQ7MzGFmPr5vmDFIYj1mr1WYfrHPovA9VVOqbC7e/1rS9ZlrAVDYHig5WB0oPtBI0TNrUiC5yhP9jeF4X8NPcWfopoY48XT39PjjXeF0vWkZqOjd7LJYrmGasHPCCJbHwhS9/F8M4s8baid764Xi0Ilfp5voorpJfn2wwx/r3l77TwZUvR+qajXVn8PnvocYfXYH6k2ioOaCpaIdf11ivDcayyiMVudsOYqFb60gARJYHG9DbqQFmSVNjaO3K2NpAeK90ZCqtgcrjkP9aUCXp0moetDFEeRXnYCKXhm+uTW0CkBFu4JlxzZkFlbASz4CQGQVBFeEwZm8geyiMuRVntzsL3oXV+YMkvjRsydC1U+lhwZsWXgHb+oWVAEzIwvzyVlk5igsi7DymmHlHsFQR50rjl+981Jy1Fw6Gu0ObTtnU+cgs28AKgDiy+Awpj5OACBAhZ/qh2HOo6i+NeA73jUAML4/qWux8mt6NjW1w599CS9xb0mSEqQBEDAtwqALUmBaG5FV3oYPnTHMjAwetlWksyByaukxQg2wQ9FlccaK/OXA3/uAEUDp3rNIDQ1ctSk6kHh1/jRFoaL4M4snEMeD73gQx4M4PsT1IZ5AfYH68tZY7zv/ApRMY9mnuVMvAAAAAElFTkSuQmCC"
  1495. /><span class="nette-warning"><?php echo array_sum($data) ?> errors</span>
  1496. <?php
  1497. }
  1498. return ob_get_clean();
  1499. }
  1500. function getPanel()
  1501. {
  1502. ob_start();
  1503. $data = $this->data;
  1504. if ($this->id === 'dumps') {
  1505. ?>
  1506. <style>#nette-debug .nette-DumpPanel h2{font:11pt/1.5 sans-serif;margin:0;padding:2px 8px;background:#3484d2;color:white}#nette-debug .nette-DumpPanel table{width:100%}#nette-debug .nette-DumpPanel a{color:#333;background:transparent}#nette-debug .nette-DumpPanel a abbr{font-family:sans-serif;color:#999}#nette-debug .nette-DumpPanel pre.nette-dump span{color:#c16549}</style>
  1507. <h1>Dumped variables</h1>
  1508. <div class="nette-inner nette-DumpPanel">
  1509. <?php foreach ($data as $item): ?>
  1510. <?php if ($item['title']):?>
  1511. <h2><?php echo htmlspecialchars($item['title']) ?></h2>
  1512. <?php endif ?>
  1513. <table>
  1514. <?php $i = 0 ?>
  1515. <?php foreach ($item['dump'] as $key => $dump): ?>
  1516. <tr class="<?php echo $i++ % 2 ? 'nette-alt' : '' ?>">
  1517. <th><?php echo htmlspecialchars($key) ?></th>
  1518. <td><?php echo $dump ?></td>
  1519. </tr>
  1520. <?php endforeach ?>
  1521. </table>
  1522. <?php endforeach ?>
  1523. </div>
  1524. <?php
  1525. } elseif ($this->id === 'errors') {
  1526. ?>
  1527. <h1>Errors</h1>
  1528. <div class="nette-inner">
  1529. <table>
  1530. <?php $i = 0 ?>
  1531. <?php foreach ($data as $item => $count): list($message, $file, $line) = explode('|', $item) ?>
  1532. <tr class="<?php echo $i++ % 2 ? 'nette-alt' : '' ?>">
  1533. <td class="nette-right"><?php echo $count ? "$count\xC3\x97" : '' ?></td>
  1534. <td><pre><?php echo htmlspecialchars($message), ' in ', Helpers::editorLink($file, $line), ':', $line ?></pre></td>
  1535. </tr>
  1536. <?php endforeach ?>
  1537. </table>
  1538. </div>
  1539. <?php
  1540. }
  1541. return ob_get_clean();
  1542. }
  1543. }
  1544. }
  1545. namespace Nette\Utils {
  1546. use Nette;
  1547. final class SafeStream
  1548. {
  1549. const PROTOCOL = 'safe';
  1550. private $handle;
  1551. private $tempHandle;
  1552. private $file;
  1553. private $tempFile;
  1554. private $deleteFile;
  1555. private $writeError = FALSE;
  1556. static function register()
  1557. {
  1558. return stream_wrapper_register(self::PROTOCOL, __CLASS__);
  1559. }
  1560. function stream_open($path, $mode, $options, &$opened_path)
  1561. {
  1562. $path = substr($path, strlen(self::PROTOCOL)+3);
  1563. $flag = trim($mode, 'rwax+');
  1564. $mode = trim($mode, 'tb');
  1565. $use_path = (bool) (STREAM_USE_PATH & $options);
  1566. if ($mode === 'r') {
  1567. return $this->checkAndLock($this->tempHandle = fopen($path, 'r'.$flag, $use_path), LOCK_SH);
  1568. } elseif ($mode === 'r+') {
  1569. if (!$this->checkAndLock($this->handle = fopen($path, 'r'.$flag, $use_path), LOCK_EX)) {
  1570. return FALSE;
  1571. }
  1572. } elseif ($mode[0] === 'x') {
  1573. if (!$this->checkAndLock($this->handle = fopen($path, 'x'.$flag, $use_path), LOCK_EX)) {
  1574. return FALSE;
  1575. }
  1576. $this->deleteFile = TRUE;
  1577. } elseif ($mode[0] === 'w' || $mode[0] === 'a') {
  1578. if ($this->checkAndLock($this->handle = @fopen($path, 'x'.$flag, $use_path), LOCK_EX)) {
  1579. $this->deleteFile = TRUE;
  1580. } elseif (!$this->checkAndLock($this->handle = fopen($path, 'a+'.$flag, $use_path), LOCK_EX)) {
  1581. return FALSE;
  1582. }
  1583. } else {
  1584. trigger_error("Unknown mode $mode", E_USER_WARNING);
  1585. return FALSE;
  1586. }
  1587. $tmp = '~~' . lcg_value() . '.tmp';
  1588. if (!$this->tempHandle = fopen($path . $tmp, (strpos($mode, '+') ? 'x+' : 'x').$flag, $use_path)) {
  1589. $this->clean();
  1590. return FALSE;
  1591. }
  1592. $this->tempFile = realpath($path . $tmp);
  1593. $this->file = substr($this->tempFile, 0, -strlen($tmp));
  1594. if ($mode === 'r+' || $mode[0] === 'a') {
  1595. $stat = fstat($this->handle);
  1596. fseek($this->handle, 0);
  1597. if (stream_copy_to_stream($this->handle, $this->tempHandle) !== $stat['size']) {
  1598. $this->clean();
  1599. return FALSE;
  1600. }
  1601. if ($mode[0] === 'a') {
  1602. fseek($this->tempHandle, 0, SEEK_END);
  1603. }
  1604. }
  1605. return TRUE;
  1606. }
  1607. private function checkAndLock($handle, $lock)
  1608. {
  1609. if (!$handle) {
  1610. return FALSE;
  1611. } elseif (!flock($handle, $lock)) {
  1612. fclose($handle);
  1613. return FALSE;
  1614. }
  1615. return TRUE;
  1616. }
  1617. private function clean()
  1618. {
  1619. flock($this->handle, LOCK_UN);
  1620. fclose($this->handle);
  1621. if ($this->deleteFile) {
  1622. unlink($this->file);
  1623. }
  1624. if ($this->tempHandle) {
  1625. fclose($this->tempHandle);
  1626. unlink($this->tempFile);
  1627. }
  1628. }
  1629. function stream_close()
  1630. {
  1631. if (!$this->tempFile) {
  1632. flock($this->tempHandle, LOCK_UN);
  1633. fclose($this->tempHandle);
  1634. return;
  1635. }
  1636. flock($this->handle, LOCK_UN);
  1637. fclose($this->handle);
  1638. fclose($this->tempHandle);
  1639. if ($this->writeError || !rename($this->tempFile, $this->file)
  1640. ) {
  1641. unlink($this->tempFile);
  1642. if ($this->deleteFile) {
  1643. unlink($this->file);
  1644. }
  1645. }
  1646. }
  1647. function stream_read($length)
  1648. {
  1649. return fread($this->tempHandle, $length);
  1650. }
  1651. function stream_write($data)
  1652. {
  1653. $len = strlen($data);
  1654. $res = fwrite($this->tempHandle, $data, $len);
  1655. if ($res !== $len) {
  1656. $this->writeError = TRUE;
  1657. }
  1658. return $res;
  1659. }
  1660. function stream_tell()
  1661. {
  1662. return ftell($this->tempHandle);
  1663. }
  1664. function stream_eof()
  1665. {
  1666. return feof($this->tempHandle);
  1667. }
  1668. function stream_seek($offset, $whence)
  1669. {
  1670. return fseek($this->tempHandle, $offset, $whence) === 0;
  1671. }
  1672. function stream_stat()
  1673. {
  1674. return fstat($this->tempHandle);
  1675. }
  1676. function url_stat($path, $flags)
  1677. {
  1678. $path = substr($path, strlen(self::PROTOCOL)+3);
  1679. return ($flags & STREAM_URL_STAT_LINK) ? @lstat($path) : @stat($path);
  1680. }
  1681. function unlink($path)
  1682. {
  1683. $path = substr($path, strlen(self::PROTOCOL)+3);
  1684. return unlink($path);
  1685. }
  1686. }
  1687. }
  1688. namespace Nette\Application {
  1689. use Nette;
  1690. class Application extends Nette\Object
  1691. {
  1692. public static $maxLoop = 20;
  1693. public $catchExceptions;
  1694. public $errorPresenter;
  1695. public $onStartup;
  1696. public $onShutdown;
  1697. public $onRequest;
  1698. public $onResponse;
  1699. public $onError;
  1700. public $allowedMethods = array('GET', 'POST', 'HEAD', 'PUT', 'DELETE');
  1701. private $requests = array();
  1702. private $presenter;
  1703. private $context;
  1704. function __construct(Nette\DI\IContainer $context)
  1705. {
  1706. $this->context = $context;
  1707. }
  1708. function run()
  1709. {
  1710. $httpRequest = $this->context->httpRequest;
  1711. $httpResponse = $this->context->httpResponse;
  1712. if ($this->allowedMethods) {
  1713. $method = $httpRequest->getMethod();
  1714. if (!in_array($method, $this->allowedMethods, TRUE)) {
  1715. $httpResponse->setCode(Nette\Http\IResponse::S501_NOT_IMPLEMENTED);
  1716. $httpResponse->setHeader('Allow', implode(',', $this->allowedMethods));
  1717. echo '<h1>Method ' . htmlSpecialChars($method) . ' is not implemented</h1>';
  1718. return;
  1719. }
  1720. }
  1721. $request = NULL;
  1722. $repeatedError = FALSE;
  1723. do {
  1724. try {
  1725. if (count($this->requests) > self::$maxLoop) {
  1726. throw new ApplicationException('Too many loops detected in application life cycle.');
  1727. }
  1728. if (!$request) {
  1729. $this->onStartup($this);
  1730. $router = $this->getRouter();
  1731. Diagnostics\RoutingPanel::initialize($this, $httpRequest);
  1732. $request = $router->match($httpRequest);
  1733. if (!$request instanceof Request) {
  1734. $request = NULL;
  1735. throw new BadRequestException('No route for HTTP request.');
  1736. }
  1737. if (strcasecmp($request->getPresenterName(), $this->errorPresenter) === 0) {
  1738. throw new BadRequestException('Invalid request. Presenter is not achievable.');
  1739. }
  1740. }
  1741. $this->requests[] = $request;
  1742. $this->onRequest($this, $request);
  1743. $presenterName = $request->getPresenterName();
  1744. try {
  1745. $this->presenter = $this->getPresenterFactory()->createPresenter($presenterName);
  1746. } catch (InvalidPresenterException $e) {
  1747. throw new BadRequestException($e->getMessage(), 404, $e);
  1748. }
  1749. $this->getPresenterFactory()->getPresenterClass($presenterName);
  1750. $request->setPresenterName($presenterName);
  1751. $request->freeze();
  1752. $response = $this->presenter->run($request);
  1753. $this->onResponse($this, $response);
  1754. if ($response instanceof Responses\ForwardResponse) {
  1755. $request = $response->getRequest();
  1756. continue;
  1757. } elseif ($response instanceof IResponse) {
  1758. $response->send($httpRequest, $httpResponse);
  1759. }
  1760. break;
  1761. } catch (\Exception $e) {
  1762. $this->onError($this, $e);
  1763. if (!$this->catchExceptions) {
  1764. $this->onShutdown($this, $e);
  1765. throw $e;
  1766. }
  1767. if ($repeatedError) {
  1768. $e = new ApplicationException('An error occurred while executing error-presenter', 0, $e);
  1769. }
  1770. if (!$httpResponse->isSent()) {
  1771. $httpResponse->setCode($e instanceof BadRequestException ? $e->getCode() : 500);
  1772. }
  1773. if (!$repeatedError && $this->errorPresenter) {
  1774. $repeatedError = TRUE;
  1775. if ($this->presenter instanceof UI\Presenter) {
  1776. try {
  1777. $this->presenter->forward(":$this->errorPresenter:", array('exception' => $e));
  1778. } catch (AbortException $foo) {
  1779. $request = $this->presenter->getLastCreatedRequest();
  1780. }
  1781. } else {
  1782. $request = new Request(
  1783. $this->errorPresenter,
  1784. Request::FORWARD,
  1785. array('exception' => $e)
  1786. );
  1787. }
  1788. } else {
  1789. if ($e instanceof BadRequestException) {
  1790. $code = $e->getCode();
  1791. } else {
  1792. $code = 500;
  1793. Nette\Diagnostics\Debugger::log($e, Nette\Diagnostics\Debugger::ERROR);
  1794. }
  1795. $messages = array(
  1796. 0 => array('Oops...', 'Your browser sent a request that this server could not understand or process.'),
  1797. 403 => array('Access Denied', 'You do not have permission to view this page. Please try contact the web site administrator if you believe you should be able to view this page.'),
  1798. 404 => array('Page Not Found', 'The page you requested could not be found. It is possible that the address is incorrect, or that the page no longer exists. Please use a search engine to find what you are looking for.'),
  1799. 405 => array('Method Not Allowed', 'The requested method is not allowed for the URL.'),
  1800. 410 => array('Page Not Found', 'The page you requested has been taken off the site. We apologize for the inconvenience.'),
  1801. 500 => array('Server Error', 'We\'re sorry! The server encountered an internal error and was unable to complete your request. Please try again later.'),
  1802. );
  1803. $message = isset($messages[$code]) ? $messages[$code] : $messages[0];
  1804. ?>
  1805. <!DOCTYPE html>
  1806. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  1807. <meta name=robots content=noindex><meta name=generator content="Nette Framework">
  1808. <style>body{color:#333;background:white;width:500px;margin:100px auto}h1{font:bold 47px/1.5 sans-serif;margin:.6em 0}p{font:21px/1.5 Georgia,serif;margin:1.5em 0}small{font-size:70%;color:gray}</style>
  1809. <title><?php echo $message[0] ?></title>
  1810. <h1><?php echo $message[0] ?></h1>
  1811. <p><?php echo $message[1] ?></p>
  1812. <?php if ($code): ?><p><small>error <?php echo $code ?></small></p><?php endif ?>
  1813. <?php
  1814. break;
  1815. }
  1816. }
  1817. } while (1);
  1818. $this->onShutdown($this, isset($e) ? $e : NULL);
  1819. }
  1820. final function getRequests()
  1821. {
  1822. return $this->requests;
  1823. }
  1824. final function getPresenter()
  1825. {
  1826. return $this->presenter;
  1827. }
  1828. final function getContext()
  1829. {
  1830. return $this->context;
  1831. }
  1832. function getRouter()
  1833. {
  1834. return $this->context->router;
  1835. }
  1836. function getPresenterFactory()
  1837. {
  1838. return $this->context->presenterFactory;
  1839. }
  1840. function storeRequest($expiration = '+ 10 minutes')
  1841. {
  1842. $session = $this->context->session->getSection('Nette.Application/requests');
  1843. do {
  1844. $key = Nette\Utils\Strings::random(5);
  1845. } while (isset($session[$key]));
  1846. $session[$key] = end($this->requests);
  1847. $session->setExpiration($expiration, $key);
  1848. return $key;
  1849. }
  1850. function restoreRequest($key)
  1851. {
  1852. $session = $this->context->session->getSection('Nette.Application/requests');
  1853. if (isset($session[$key])) {
  1854. $request = clone $session[$key];
  1855. unset($session[$key]);
  1856. $request->setFlag(Request::RESTORED, TRUE);
  1857. $this->presenter->sendResponse(new Responses\ForwardResponse($request));
  1858. }
  1859. }
  1860. }
  1861. }
  1862. namespace Nette\Application\Diagnostics {
  1863. use Nette;use Nette\Application\Routers;use Nette\Application\UI\Presenter;
  1864. use Nette\Diagnostics\Debugger;
  1865. class RoutingPanel extends Nette\Object implements Nette\Diagnostics\IBarPanel
  1866. {
  1867. private $router;
  1868. private $httpRequest;
  1869. private $routers = array();
  1870. private $request;
  1871. static function initialize(Nette\Application\Application $application, Nette\Http\IRequest $httpRequest)
  1872. {
  1873. Debugger::$bar->addPanel(new self($application->getRouter(), $httpRequest));
  1874. Debugger::$blueScreen->addPanel(function($e) use($application) {
  1875. if ($e === NULL) {
  1876. return array(
  1877. 'tab' => 'Nette Application',
  1878. 'panel' => '<h3>Requests</h3>' . Nette\Diagnostics\Helpers::clickableDump($application->getRequests())
  1879. . '<h3>Presenter</h3>' . Nette\Diagnostics\Helpers::clickableDump($application->getPresenter())
  1880. );
  1881. }
  1882. });
  1883. }
  1884. function __construct(Nette\Application\IRouter $router, Nette\Http\IRequest $httpRequest)
  1885. {
  1886. $this->router = $router;
  1887. $this->httpRequest = $httpRequest;
  1888. }
  1889. function getTab()
  1890. {
  1891. $this->analyse($this->router);
  1892. ob_start();
  1893. ?>
  1894. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJHSURBVDjLlZPNi81hFMc/z7137p1mTCFvNZfGSzLIWNjZKRvFRoqNhRCSYm8xS3+AxRRZ2JAFJWJHSQqTQkbEzYwIM+6Yid/znJfH4prLXShOnb6r8/nWOd8Tcs78bz0/f+KMu50y05nK/wy+uHDylbutqS5extvGcxaWqtoGDA8PZ3dnrs2srQc2Zko41UXLmLdyDW5OfvsUkUgbYGbU63UAQggdmvMzFmzZCgTi7CQmkZwdEaX0JwDgTnGbTCaE0G4zw80omhPI92lcEtkNkdgJCCHwJX7mZvNaB0A14SaYJlwTrpHsTkoFlV1nt2c3x5YYo1/vM9A/gKpxdfwyu/v3teCayKq4JEwT5EB2R6WgYmrs2bYbcUNNUVfEhIfFYy69uci+1fuRX84mkawFSxd/4nVWUopUVIykwlQxRTJBTIDA4Pp1jBZPuNW4wUAPmCqWIn29X1k4f5Ku8g9mpKCkakRLVEs1auVuauVuyqHMo8ejNCe+sWPVTkQKXCMmkeZUmUZjETF1tc6ooly+fgUVw9So1/tRN6YnZji46QghBFKKuAouERNhMlbAHZFE6e7pB+He8MMw+GGI4xtOMf1+lsl3TQ4NHf19BSlaO1DB9BfMHdX0O0iqSgiBbJkjm491hClJbA1LxCURgpPzXwAHhg63necAIi3XngXLcRU0fof8ETMljIyM5LGxMcbHxzvy/6fuXdWgt6+PWncv1e4euqo1ZmabvHs5+jn8yzufO7hiiZmuNpNBM13rbvVSpbrXJE7/BMkHtU9jFIC/AAAAAElFTkSuQmCC"
  1895. /><?php if (empty($this->request)): ?>no route<?php else: echo $this->request->getPresenterName() . ':' . (isset($this->request->params[Presenter::ACTION_KEY]) ? $this->request->params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->params[Presenter::SIGNAL_KEY]) ? " {$this->request->params[Presenter::SIGNAL_KEY]}!" : ''); endif ?>
  1896. <?php
  1897. return ob_get_clean();
  1898. }
  1899. function getPanel()
  1900. {
  1901. ob_start();
  1902. ?>
  1903. <style>#nette-debug .nette-RoutingPanel table{font:9pt/1.5 Consolas,monospace}#nette-debug .nette-RoutingPanel .yes td{color:green}#nette-debug .nette-RoutingPanel .may td{color:#67F}#nette-debug .nette-RoutingPanel pre,#nette-debug .nette-RoutingPanel code{display:inline}</style>
  1904. <div class="nette-RoutingPanel">
  1905. <h1>
  1906. <?php if (empty($this->request)): ?>
  1907. no route
  1908. <?php else: ?>
  1909. <?php echo $this->request->getPresenterName() . ':' . (isset($this->request->params[Presenter::ACTION_KEY]) ? $this->request->params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->params[Presenter::SIGNAL_KEY]) ? " {$this->request->params[Presenter::SIGNAL_KEY]}!" : '') ?>
  1910. <?php endif ?>
  1911. </h1>
  1912. <?php if (!empty($this->request)): ?>
  1913. <?php $params = $this->request->getParams() ?>
  1914. <?php if (empty($params)): ?>
  1915. <p>No parameters.</p>
  1916. <?php else: ?>
  1917. <table>
  1918. <thead>
  1919. <tr>
  1920. <th>Parameter</th>
  1921. <th>Value</th>
  1922. </tr>
  1923. </thead>
  1924. <tbody>
  1925. <?php unset($params[Presenter::ACTION_KEY], $params[Presenter::SIGNAL_KEY]) ?>
  1926. <?php foreach ($params as $key => $value): ?>
  1927. <tr>
  1928. <td><code><?php echo htmlSpecialChars($key) ?></code></td>
  1929. <td><?php if (is_string($value)):?><code><?php echo htmlSpecialChars($value) ?></code><?php else: echo Debugger::dump($value, TRUE); endif ?></td>
  1930. </tr>
  1931. <?php endforeach ?>
  1932. </tbody>
  1933. </table>
  1934. <?php endif ?>
  1935. <?php endif ?>
  1936. <h2>Routers</h2>
  1937. <?php if (empty($this->routers)): ?>
  1938. <p>No routers defined.</p>
  1939. <?php else: ?>
  1940. <div class="nette-inner">
  1941. <table>
  1942. <thead>
  1943. <tr>
  1944. <th>Matched?</th>
  1945. <th>Class</th>
  1946. <th>Mask</th>
  1947. <th>Defaults</th>
  1948. <th>Request</th>
  1949. </tr>
  1950. </thead>
  1951. <tbody>
  1952. <?php foreach ($this->routers as $router): ?>
  1953. <tr class="<?php echo $router['matched'] ?>">
  1954. <td><?php echo $router['matched'] ?></td>
  1955. <td><code title="<?php echo htmlSpecialChars($router['class']) ?>"><?php echo preg_replace('#.+\\\\#', '', htmlSpecialChars($router['class'])) ?></code></td>
  1956. <td><code><strong><?php echo htmlSpecialChars($router['mask']) ?></strong></code></td>
  1957. <td><code>
  1958. <?php foreach ($router['defaults'] as $key => $value): ?>
  1959. <?php echo htmlSpecialChars($key), "&nbsp;=&nbsp;", is_string($value) ? htmlSpecialChars($value) : str_replace("\n</pre", '</pre', Debugger::dump($value, TRUE)) ?><br />
  1960. <?php endforeach ?>
  1961. </code></td>
  1962. <td><?php if ($router['request']): ?><code>
  1963. <?php $params = $router['request']->getParams(); ?>
  1964. <strong><?php echo htmlSpecialChars($router['request']->getPresenterName() . ':' . (isset($params[Presenter::ACTION_KEY]) ? $params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION)) ?></strong><br />
  1965. <?php unset($params[Presenter::ACTION_KEY]) ?>
  1966. <?php foreach ($params as $key => $value): ?>
  1967. <?php echo htmlSpecialChars($key), "&nbsp;=&nbsp;", is_string($value) ? htmlSpecialChars($value) : str_replace("\n</pre", '</pre', Debugger::dump($value, TRUE)) ?><br />
  1968. <?php endforeach ?>
  1969. </code><?php endif ?></td>
  1970. </tr>
  1971. <?php endforeach ?>
  1972. </tbody>
  1973. </table>
  1974. </div>
  1975. <?php endif ?>
  1976. </div>
  1977. <?php
  1978. return ob_get_clean();
  1979. }
  1980. private function analyse($router)
  1981. {
  1982. if ($router instanceof Routers\RouteList) {
  1983. foreach ($router as $subRouter) {
  1984. $this->analyse($subRouter);
  1985. }
  1986. return;
  1987. }
  1988. $request = $router->match($this->httpRequest);
  1989. $matched = $request === NULL ? 'no' : 'may';
  1990. if ($request !== NULL && empty($this->request)) {
  1991. $this->request = $request;
  1992. $matched = 'yes';
  1993. }
  1994. $this->routers[] = array(
  1995. 'matched' => $matched,
  1996. 'class' => get_class($router),
  1997. 'defaults' => $router instanceof Routers\Route || $router instanceof Routers\SimpleRouter ? $router->getDefaults() : array(),
  1998. 'mask' => $router instanceof Routers\Route ? $router->getMask() : NULL,
  1999. 'request' => $request,
  2000. );
  2001. }
  2002. }
  2003. }
  2004. namespace Nette\Application {
  2005. use Nette;
  2006. class AbortException extends \Exception
  2007. {
  2008. }
  2009. class ApplicationException extends \Exception
  2010. {
  2011. }
  2012. class InvalidPresenterException extends \Exception
  2013. {
  2014. }
  2015. class BadRequestException extends \Exception
  2016. {
  2017. protected $defaultCode = 404;
  2018. function __construct($message = '', $code = 0, \Exception $previous = NULL)
  2019. {
  2020. if ($code < 200 || $code > 504) {
  2021. $code = $this->defaultCode;
  2022. }
  2023. {
  2024. parent::__construct($message, $code, $previous);
  2025. }
  2026. }
  2027. }
  2028. class ForbiddenRequestException extends BadRequestException
  2029. {
  2030. protected $defaultCode = 403;
  2031. }
  2032. }
  2033. namespace NetteModule {
  2034. use Nette;use Nette\Application;use Nette\Application\Responses;use Nette\Http;
  2035. class MicroPresenter extends Nette\Object implements Application\IPresenter
  2036. {
  2037. private $context;
  2038. private $request;
  2039. function run(Application\Request $request)
  2040. {
  2041. $this->request = $request;
  2042. $httpRequest = $this->context->httpRequest;
  2043. if (!$httpRequest->isAjax() && ($request->isMethod('get') || $request->isMethod('head'))) {
  2044. $refUrl = clone $httpRequest->getUrl();
  2045. $url = $this->context->router->constructUrl($request, $refUrl->setPath($refUrl->getScriptPath()));
  2046. if ($url !== NULL && !$httpRequest->getUrl()->isEqual($url)) {
  2047. return new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY);
  2048. }
  2049. }
  2050. $params = $request->getParams();
  2051. if (!isset($params['callback'])) {
  2052. return;
  2053. }
  2054. $params['presenter'] = $this;
  2055. $response = callback($params['callback'])->invokeNamedArgs($params);
  2056. if (is_string($response)) {
  2057. $response = array($response, array());
  2058. }
  2059. if (is_array($response)) {
  2060. if ($response instanceof \SplFileInfo) {
  2061. $response = $this->createTemplate('Nette\Templating\FileTemplate')
  2062. ->setParams($response[1])->setFile($response[0]);
  2063. } else {
  2064. $response = $this->createTemplate('Nette\Templating\Template')
  2065. ->setParams($response[1])->setSource($response[0]);
  2066. }
  2067. }
  2068. if ($response instanceof Nette\Templating\ITemplate) {
  2069. return new Responses\TextResponse($response);
  2070. } else {
  2071. return $response;
  2072. }
  2073. }
  2074. function createTemplate($class = NULL, $latteFactory = NULL)
  2075. {
  2076. $template = $class ? new $class : new Nette\Templating\FileTemplate;
  2077. $template->setParams($this->request->getParams());
  2078. $template->presenter = $this;
  2079. $template->context = $context = $this->context;
  2080. $url = $context->httpRequest->getUrl();
  2081. $template->baseUrl = rtrim($url->getBaseUrl(), '/');
  2082. $template->basePath = rtrim($url->getBasePath(), '/');
  2083. $template->registerHelperLoader('Nette\Templating\DefaultHelpers::loader');
  2084. $template->setCacheStorage($context->templateCacheStorage);
  2085. $template->onPrepareFilters[] = function($template) use($latteFactory, $context) {
  2086. $template->registerFilter($latteFactory ? $latteFactory() : new Nette\Latte\Engine);
  2087. };
  2088. return $template;
  2089. }
  2090. function redirectUrl($url, $code = Http\IResponse::S302_FOUND)
  2091. {
  2092. return new Responses\RedirectResponse($url, $code);
  2093. }
  2094. function error($code, $message = NULL)
  2095. {
  2096. throw new Application\BadRequestException($message, $code);
  2097. }
  2098. function getRequest()
  2099. {
  2100. return $this->request;
  2101. }
  2102. function setContext(Nette\DI\IContainer $context)
  2103. {
  2104. $this->context = $context;
  2105. return $this;
  2106. }
  2107. final function getContext()
  2108. {
  2109. return $this->context;
  2110. }
  2111. }
  2112. }
  2113. namespace Nette\Application {
  2114. use Nette;
  2115. class PresenterFactory implements IPresenterFactory
  2116. {
  2117. public $caseSensitive = FALSE;
  2118. private $baseDir;
  2119. private $cache = array();
  2120. private $context;
  2121. function __construct($baseDir, Nette\DI\IContainer $context)
  2122. {
  2123. $this->baseDir = $baseDir;
  2124. $this->context = $context;
  2125. }
  2126. function createPresenter($name)
  2127. {
  2128. $class = $this->getPresenterClass($name);
  2129. $presenter = new $class;
  2130. $presenter->setContext($this->context);
  2131. return $presenter;
  2132. }
  2133. function getPresenterClass(& $name)
  2134. {
  2135. if (isset($this->cache[$name])) {
  2136. list($class, $name) = $this->cache[$name];
  2137. return $class;
  2138. }
  2139. if (!is_string($name) || !Nette\Utils\Strings::match($name, "#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#")) {
  2140. throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid.");
  2141. }
  2142. $class = $this->formatPresenterClass($name);
  2143. if (!class_exists($class)) {
  2144. $file = $this->formatPresenterFile($name);
  2145. if (is_file($file) && is_readable($file)) {
  2146. Nette\Utils\LimitedScope::load($file);
  2147. }
  2148. if (!class_exists($class)) {
  2149. throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found in '$file'.");
  2150. }
  2151. }
  2152. $reflection = new Nette\Reflection\ClassType($class);
  2153. $class = $reflection->getName();
  2154. if (!$reflection->implementsInterface('Nette\Application\IPresenter')) {
  2155. throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor.");
  2156. }
  2157. if ($reflection->isAbstract()) {
  2158. throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract.");
  2159. }
  2160. $realName = $this->unformatPresenterClass($class);
  2161. if ($name !== $realName) {
  2162. if ($this->caseSensitive) {
  2163. throw new InvalidPresenterException("Cannot load presenter '$name', case mismatch. Real name is '$realName'.");
  2164. } else {
  2165. $this->cache[$name] = array($class, $realName);
  2166. $name = $realName;
  2167. }
  2168. } else {
  2169. $this->cache[$name] = array($class, $realName);
  2170. }
  2171. return $class;
  2172. }
  2173. function formatPresenterClass($presenter)
  2174. {
  2175. return str_replace(':', 'Module\\', $presenter) . 'Presenter';
  2176. }
  2177. function unformatPresenterClass($class)
  2178. {
  2179. return str_replace('Module\\', ':', substr($class, 0, -9));
  2180. }
  2181. function formatPresenterFile($presenter)
  2182. {
  2183. $path = '/' . str_replace(':', 'Module/', $presenter);
  2184. return $this->baseDir . substr_replace($path, '/presenters', strrpos($path, '/'), 0) . 'Presenter.php';
  2185. }
  2186. }
  2187. }
  2188. namespace Nette {
  2189. use Nette;
  2190. abstract class FreezableObject extends Object implements IFreezable
  2191. {
  2192. private $frozen = FALSE;
  2193. function freeze()
  2194. {
  2195. $this->frozen = TRUE;
  2196. }
  2197. final function isFrozen()
  2198. {
  2199. return $this->frozen;
  2200. }
  2201. function __clone()
  2202. {
  2203. $this->frozen = FALSE;
  2204. }
  2205. protected function updating()
  2206. {
  2207. if ($this->frozen) {
  2208. $class = get_class($this);
  2209. throw new InvalidStateException("Cannot modify a frozen object $class.");
  2210. }
  2211. }
  2212. }
  2213. }
  2214. namespace Nette\Application {
  2215. use Nette;
  2216. final class Request extends Nette\FreezableObject
  2217. {
  2218. const FORWARD = 'FORWARD';
  2219. const SECURED = 'secured';
  2220. const RESTORED = 'restored';
  2221. private $method;
  2222. private $flags = array();
  2223. private $name;
  2224. private $params;
  2225. private $post;
  2226. private $files;
  2227. function __construct($name, $method, array $params, array $post = array(), array $files = array(), array $flags = array())
  2228. {
  2229. $this->name = $name;
  2230. $this->method = $method;
  2231. $this->params = $params;
  2232. $this->post = $post;
  2233. $this->files = $files;
  2234. $this->flags = $flags;
  2235. }
  2236. function setPresenterName($name)
  2237. {
  2238. $this->updating();
  2239. $this->name = $name;
  2240. return $this;
  2241. }
  2242. function getPresenterName()
  2243. {
  2244. return $this->name;
  2245. }
  2246. function setParams(array $params)
  2247. {
  2248. $this->updating();
  2249. $this->params = $params;
  2250. return $this;
  2251. }
  2252. function getParams()
  2253. {
  2254. return $this->params;
  2255. }
  2256. function setPost(array $params)
  2257. {
  2258. $this->updating();
  2259. $this->post = $params;
  2260. return $this;
  2261. }
  2262. function getPost()
  2263. {
  2264. return $this->post;
  2265. }
  2266. function setFiles(array $files)
  2267. {
  2268. $this->updating();
  2269. $this->files = $files;
  2270. return $this;
  2271. }
  2272. function getFiles()
  2273. {
  2274. return $this->files;
  2275. }
  2276. function setMethod($method)
  2277. {
  2278. $this->method = $method;
  2279. return $this;
  2280. }
  2281. function getMethod()
  2282. {
  2283. return $this->method;
  2284. }
  2285. function isMethod($method)
  2286. {
  2287. return strcasecmp($this->method, $method) === 0;
  2288. }
  2289. function isPost()
  2290. {
  2291. return strcasecmp($this->method, 'post') === 0;
  2292. }
  2293. function setFlag($flag, $value = TRUE)
  2294. {
  2295. $this->updating();
  2296. $this->flags[$flag] = (bool) $value;
  2297. return $this;
  2298. }
  2299. function hasFlag($flag)
  2300. {
  2301. return !empty($this->flags[$flag]);
  2302. }
  2303. }
  2304. }
  2305. namespace Nette\Application\Responses {
  2306. use Nette;
  2307. class FileResponse extends Nette\Object implements Nette\Application\IResponse
  2308. {
  2309. private $file;
  2310. private $contentType;
  2311. private $name;
  2312. public $resuming = TRUE;
  2313. function __construct($file, $name = NULL, $contentType = NULL)
  2314. {
  2315. if (!is_file($file)) {
  2316. throw new Nette\Application\BadRequestException("File '$file' doesn't exist.");
  2317. }
  2318. $this->file = $file;
  2319. $this->name = $name ? $name : basename($file);
  2320. $this->contentType = $contentType ? $contentType : 'application/octet-stream';
  2321. }
  2322. final function getFile()
  2323. {
  2324. return $this->file;
  2325. }
  2326. final function getName()
  2327. {
  2328. return $this->name;
  2329. }
  2330. final function getContentType()
  2331. {
  2332. return $this->contentType;
  2333. }
  2334. function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
  2335. {
  2336. $httpResponse->setContentType($this->contentType);
  2337. $httpResponse->setHeader('Content-Disposition', 'attachment; filename="' . $this->name . '"');
  2338. $filesize = $length = filesize($this->file);
  2339. $handle = fopen($this->file, 'r');
  2340. if ($this->resuming) {
  2341. $httpResponse->setHeader('Accept-Ranges', 'bytes');
  2342. $range = $httpRequest->getHeader('Range');
  2343. if ($range !== NULL) {
  2344. $range = substr($range, 6);
  2345. list($start, $end) = explode('-', $range);
  2346. if ($start == NULL) {
  2347. $start = 0;
  2348. }
  2349. if ($end == NULL) {
  2350. $end = $filesize - 1;
  2351. }
  2352. if ($start < 0 || $end <= $start || $end > $filesize -1) {
  2353. $httpResponse->setCode(416);
  2354. return;
  2355. }
  2356. $httpResponse->setCode(206);
  2357. $httpResponse->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $filesize);
  2358. $length = $end - $start + 1;
  2359. fseek($handle, $start);
  2360. } else {
  2361. $httpResponse->setHeader('Content-Range', 'bytes 0-' . ($filesize - 1) . '/' . $filesize);
  2362. }
  2363. }
  2364. $httpResponse->setHeader('Content-Length', $length);
  2365. while (!feof($handle)) {
  2366. echo fread($handle, 4e6);
  2367. }
  2368. fclose($handle);
  2369. }
  2370. }
  2371. class ForwardResponse extends Nette\Object implements Nette\Application\IResponse
  2372. {
  2373. private $request;
  2374. function __construct(Nette\Application\Request $request)
  2375. {
  2376. $this->request = $request;
  2377. }
  2378. final function getRequest()
  2379. {
  2380. return $this->request;
  2381. }
  2382. function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
  2383. {
  2384. }
  2385. }
  2386. class JsonResponse extends Nette\Object implements Nette\Application\IResponse
  2387. {
  2388. private $payload;
  2389. private $contentType;
  2390. function __construct($payload, $contentType = NULL)
  2391. {
  2392. if (!is_array($payload) && !is_object($payload)) {
  2393. throw new Nette\InvalidArgumentException("Payload must be array or object class, " . gettype($payload) . " given.");
  2394. }
  2395. $this->payload = $payload;
  2396. $this->contentType = $contentType ? $contentType : 'application/json';
  2397. }
  2398. final function getPayload()
  2399. {
  2400. return $this->payload;
  2401. }
  2402. final function getContentType()
  2403. {
  2404. return $this->contentType;
  2405. }
  2406. function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
  2407. {
  2408. $httpResponse->setContentType($this->contentType);
  2409. $httpResponse->setExpiration(FALSE);
  2410. echo Nette\Utils\Json::encode($this->payload);
  2411. }
  2412. }
  2413. use Nette\Http;
  2414. class RedirectResponse extends Nette\Object implements Nette\Application\IResponse
  2415. {
  2416. private $url;
  2417. private $code;
  2418. function __construct($url, $code = Http\IResponse::S302_FOUND)
  2419. {
  2420. $this->url = (string) $url;
  2421. $this->code = (int) $code;
  2422. }
  2423. final function getUrl()
  2424. {
  2425. return $this->url;
  2426. }
  2427. final function getCode()
  2428. {
  2429. return $this->code;
  2430. }
  2431. function send(Http\IRequest $httpRequest, Http\IResponse $httpResponse)
  2432. {
  2433. $httpResponse->redirect($this->url, $this->code);
  2434. }
  2435. }
  2436. class TextResponse extends Nette\Object implements Nette\Application\IResponse
  2437. {
  2438. private $source;
  2439. function __construct($source)
  2440. {
  2441. $this->source = $source;
  2442. }
  2443. final function getSource()
  2444. {
  2445. return $this->source;
  2446. }
  2447. function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
  2448. {
  2449. if ($this->source instanceof Nette\Templating\ITemplate) {
  2450. $this->source->render();
  2451. } else {
  2452. echo $this->source;
  2453. }
  2454. }
  2455. }
  2456. }
  2457. namespace Nette\Application\Routers {
  2458. use Nette;use Nette\Application;
  2459. class CliRouter extends Nette\Object implements Application\IRouter
  2460. {
  2461. const PRESENTER_KEY = 'action';
  2462. private $defaults;
  2463. function __construct($defaults = array())
  2464. {
  2465. $this->defaults = $defaults;
  2466. }
  2467. function match(Nette\Http\IRequest $httpRequest)
  2468. {
  2469. if (empty($_SERVER['argv']) || !is_array($_SERVER['argv'])) {
  2470. return NULL;
  2471. }
  2472. $names = array(self::PRESENTER_KEY);
  2473. $params = $this->defaults;
  2474. $args = $_SERVER['argv'];
  2475. array_shift($args);
  2476. $args[] = '--';
  2477. foreach ($args as $arg) {
  2478. $opt = preg_replace('#/|-+#A', '', $arg);
  2479. if ($opt === $arg) {
  2480. if (isset($flag) || $flag = array_shift($names)) {
  2481. $params[$flag] = $arg;
  2482. } else {
  2483. $params[] = $arg;
  2484. }
  2485. $flag = NULL;
  2486. continue;
  2487. }
  2488. if (isset($flag)) {
  2489. $params[$flag] = TRUE;
  2490. $flag = NULL;
  2491. }
  2492. if ($opt !== '') {
  2493. $pair = explode('=', $opt, 2);
  2494. if (isset($pair[1])) {
  2495. $params[$pair[0]] = $pair[1];
  2496. } else {
  2497. $flag = $pair[0];
  2498. }
  2499. }
  2500. }
  2501. if (!isset($params[self::PRESENTER_KEY])) {
  2502. throw new Nette\InvalidStateException('Missing presenter & action in route definition.');
  2503. }
  2504. $presenter = $params[self::PRESENTER_KEY];
  2505. if ($a = strrpos($presenter, ':')) {
  2506. $params[self::PRESENTER_KEY] = substr($presenter, $a + 1);
  2507. $presenter = substr($presenter, 0, $a);
  2508. }
  2509. return new Application\Request(
  2510. $presenter,
  2511. 'CLI',
  2512. $params
  2513. );
  2514. }
  2515. function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl)
  2516. {
  2517. return NULL;
  2518. }
  2519. function getDefaults()
  2520. {
  2521. return $this->defaults;
  2522. }
  2523. }
  2524. use Nette\Utils\Strings;
  2525. class Route extends Nette\Object implements Application\IRouter
  2526. {
  2527. const PRESENTER_KEY = 'presenter';
  2528. const MODULE_KEY = 'module';
  2529. const CASE_SENSITIVE = 256;
  2530. const HOST = 1,
  2531. PATH = 2,
  2532. RELATIVE = 3;
  2533. const VALUE = 'value';
  2534. const PATTERN = 'pattern';
  2535. const FILTER_IN = 'filterIn';
  2536. const FILTER_OUT = 'filterOut';
  2537. const FILTER_TABLE = 'filterTable';
  2538. const OPTIONAL = 0,
  2539. PATH_OPTIONAL = 1,
  2540. CONSTANT = 2;
  2541. public static $defaultFlags = 0;
  2542. public static $styles = array(
  2543. '#' => array(
  2544. self::PATTERN => '[^/]+',
  2545. self::FILTER_IN => 'rawurldecode',
  2546. self::FILTER_OUT => 'rawurlencode',
  2547. ),
  2548. '?#' => array(
  2549. ),
  2550. 'module' => array(
  2551. self::PATTERN => '[a-z][a-z0-9.-]*',
  2552. self::FILTER_IN => array(__CLASS__, 'path2presenter'),
  2553. self::FILTER_OUT => array(__CLASS__, 'presenter2path'),
  2554. ),
  2555. 'presenter' => array(
  2556. self::PATTERN => '[a-z][a-z0-9.-]*',
  2557. self::FILTER_IN => array(__CLASS__, 'path2presenter'),
  2558. self::FILTER_OUT => array(__CLASS__, 'presenter2path'),
  2559. ),
  2560. 'action' => array(
  2561. self::PATTERN => '[a-z][a-z0-9-]*',
  2562. self::FILTER_IN => array(__CLASS__, 'path2action'),
  2563. self::FILTER_OUT => array(__CLASS__, 'action2path'),
  2564. ),
  2565. '?module' => array(
  2566. ),
  2567. '?presenter' => array(
  2568. ),
  2569. '?action' => array(
  2570. ),
  2571. );
  2572. private $mask;
  2573. private $sequence;
  2574. private $re;
  2575. private $metadata = array();
  2576. private $xlat;
  2577. private $type;
  2578. private $flags;
  2579. function __construct($mask, $metadata = array(), $flags = 0)
  2580. {
  2581. if (is_string($metadata)) {
  2582. $a = strrpos($metadata, ':');
  2583. if (!$a) {
  2584. throw new Nette\InvalidArgumentException("Second argument must be array or string in format Presenter:action, '$metadata' given.");
  2585. }
  2586. $metadata = array(
  2587. self::PRESENTER_KEY => substr($metadata, 0, $a),
  2588. 'action' => $a === strlen($metadata) - 1 ? Application\UI\Presenter::DEFAULT_ACTION : substr($metadata, $a + 1),
  2589. );
  2590. } elseif ($metadata instanceof \Closure || $metadata instanceof Nette\Callback) {
  2591. $metadata = array(
  2592. self::PRESENTER_KEY => 'Nette:Micro',
  2593. 'callback' => $metadata,
  2594. );
  2595. }
  2596. $this->flags = $flags | self::$defaultFlags;
  2597. $this->setMask($mask, $metadata);
  2598. }
  2599. function match(Nette\Http\IRequest $httpRequest)
  2600. {
  2601. $url = $httpRequest->getUrl();
  2602. if ($this->type === self::HOST) {
  2603. $path = '//' . $url->getHost() . $url->getPath();
  2604. } elseif ($this->type === self::RELATIVE) {
  2605. $basePath = $url->getBasePath();
  2606. if (strncmp($url->getPath(), $basePath, strlen($basePath)) !== 0) {
  2607. return NULL;
  2608. }
  2609. $path = (string) substr($url->getPath(), strlen($basePath));
  2610. } else {
  2611. $path = $url->getPath();
  2612. }
  2613. if ($path !== '') {
  2614. $path = rtrim($path, '/') . '/';
  2615. }
  2616. if (!$matches = Strings::match($path, $this->re)) {
  2617. return NULL;
  2618. }
  2619. $params = array();
  2620. foreach ($matches as $k => $v) {
  2621. if (is_string($k) && $v !== '') {
  2622. $params[str_replace('___', '-', $k)] = $v;
  2623. }
  2624. }
  2625. foreach ($this->metadata as $name => $meta) {
  2626. if (isset($params[$name])) {
  2627. } elseif (isset($meta['fixity']) && $meta['fixity'] !== self::OPTIONAL) {
  2628. $params[$name] = NULL;
  2629. }
  2630. }
  2631. if ($this->xlat) {
  2632. $params += self::renameKeys($httpRequest->getQuery(), array_flip($this->xlat));
  2633. } else {
  2634. $params += $httpRequest->getQuery();
  2635. }
  2636. foreach ($this->metadata as $name => $meta) {
  2637. if (isset($params[$name])) {
  2638. if (!is_scalar($params[$name])) {
  2639. } elseif (isset($meta[self::FILTER_TABLE][$params[$name]])) {
  2640. $params[$name] = $meta[self::FILTER_TABLE][$params[$name]];
  2641. } elseif (isset($meta[self::FILTER_IN])) {
  2642. $params[$name] = call_user_func($meta[self::FILTER_IN], (string) $params[$name]);
  2643. if ($params[$name] === NULL && !isset($meta['fixity'])) {
  2644. return NULL;
  2645. }
  2646. }
  2647. } elseif (isset($meta['fixity'])) {
  2648. $params[$name] = $meta[self::VALUE];
  2649. }
  2650. }
  2651. if (!isset($params[self::PRESENTER_KEY])) {
  2652. throw new Nette\InvalidStateException('Missing presenter in route definition.');
  2653. }
  2654. if (isset($this->metadata[self::MODULE_KEY])) {
  2655. if (!isset($params[self::MODULE_KEY])) {
  2656. throw new Nette\InvalidStateException('Missing module in route definition.');
  2657. }
  2658. $presenter = $params[self::MODULE_KEY] . ':' . $params[self::PRESENTER_KEY];
  2659. unset($params[self::MODULE_KEY], $params[self::PRESENTER_KEY]);
  2660. } else {
  2661. $presenter = $params[self::PRESENTER_KEY];
  2662. unset($params[self::PRESENTER_KEY]);
  2663. }
  2664. return new Application\Request(
  2665. $presenter,
  2666. $httpRequest->getMethod(),
  2667. $params,
  2668. $httpRequest->getPost(),
  2669. $httpRequest->getFiles(),
  2670. array(Application\Request::SECURED => $httpRequest->isSecured())
  2671. );
  2672. }
  2673. function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl)
  2674. {
  2675. if ($this->flags & self::ONE_WAY) {
  2676. return NULL;
  2677. }
  2678. $params = $appRequest->getParams();
  2679. $metadata = $this->metadata;
  2680. $presenter = $appRequest->getPresenterName();
  2681. $params[self::PRESENTER_KEY] = $presenter;
  2682. if (isset($metadata[self::MODULE_KEY])) {
  2683. $module = $metadata[self::MODULE_KEY];
  2684. if (isset($module['fixity']) && strncasecmp($presenter, $module[self::VALUE] . ':', strlen($module[self::VALUE]) + 1) === 0) {
  2685. $a = strlen($module[self::VALUE]);
  2686. } else {
  2687. $a = strrpos($presenter, ':');
  2688. }
  2689. if ($a === FALSE) {
  2690. $params[self::MODULE_KEY] = '';
  2691. } else {
  2692. $params[self::MODULE_KEY] = substr($presenter, 0, $a);
  2693. $params[self::PRESENTER_KEY] = substr($presenter, $a + 1);
  2694. }
  2695. }
  2696. foreach ($metadata as $name => $meta) {
  2697. if (!isset($params[$name])) {
  2698. continue;
  2699. }
  2700. if (isset($meta['fixity'])) {
  2701. if (is_scalar($params[$name]) ? strcasecmp($params[$name], $meta[self::VALUE]) === 0
  2702. : $params[$name] === $meta[self::VALUE]
  2703. ) {
  2704. unset($params[$name]);
  2705. continue;
  2706. } elseif ($meta['fixity'] === self::CONSTANT) {
  2707. return NULL;
  2708. }
  2709. }
  2710. if (!is_scalar($params[$name])) {
  2711. } elseif (isset($meta['filterTable2'][$params[$name]])) {
  2712. $params[$name] = $meta['filterTable2'][$params[$name]];
  2713. } elseif (isset($meta[self::FILTER_OUT])) {
  2714. $params[$name] = call_user_func($meta[self::FILTER_OUT], $params[$name]);
  2715. }
  2716. if (isset($meta[self::PATTERN]) && !preg_match($meta[self::PATTERN], rawurldecode($params[$name]))) {
  2717. return NULL;
  2718. }
  2719. }
  2720. $sequence = $this->sequence;
  2721. $brackets = array();
  2722. $required = 0;
  2723. $url = '';
  2724. $i = count($sequence) - 1;
  2725. do {
  2726. $url = $sequence[$i] . $url;
  2727. if ($i === 0) {
  2728. break;
  2729. }
  2730. $i--;
  2731. $name = $sequence[$i]; $i--;
  2732. if ($name === ']') {
  2733. $brackets[] = $url;
  2734. } elseif ($name[0] === '[') {
  2735. $tmp = array_pop($brackets);
  2736. if ($required < count($brackets) + 1) {
  2737. if ($name !== '[!') {
  2738. $url = $tmp;
  2739. }
  2740. } else {
  2741. $required = count($brackets);
  2742. }
  2743. } elseif ($name[0] === '?') {
  2744. continue;
  2745. } elseif (isset($params[$name]) && $params[$name] != '') {
  2746. $required = count($brackets);
  2747. $url = $params[$name] . $url;
  2748. unset($params[$name]);
  2749. } elseif (isset($metadata[$name]['fixity'])) {
  2750. $url = $metadata[$name]['defOut'] . $url;
  2751. } else {
  2752. return NULL;
  2753. }
  2754. } while (TRUE);
  2755. if ($this->xlat) {
  2756. $params = self::renameKeys($params, $this->xlat);
  2757. }
  2758. $sep = ini_get('arg_separator.input');
  2759. $query = http_build_query($params, '', $sep ? $sep[0] : '&');
  2760. if ($query != '') {
  2761. $url .= '?' . $query;
  2762. }
  2763. if ($this->type === self::RELATIVE) {
  2764. $url = '//' . $refUrl->getAuthority() . $refUrl->getBasePath() . $url;
  2765. } elseif ($this->type === self::PATH) {
  2766. $url = '//' . $refUrl->getAuthority() . $url;
  2767. }
  2768. if (strpos($url, '//', 2) !== FALSE) {
  2769. return NULL;
  2770. }
  2771. $url = ($this->flags & self::SECURED ? 'https:' : 'http:') . $url;
  2772. return $url;
  2773. }
  2774. private function setMask($mask, array $metadata)
  2775. {
  2776. $this->mask = $mask;
  2777. if (substr($mask, 0, 2) === '//') {
  2778. $this->type = self::HOST;
  2779. } elseif (substr($mask, 0, 1) === '/') {
  2780. $this->type = self::PATH;
  2781. } else {
  2782. $this->type = self::RELATIVE;
  2783. }
  2784. foreach ($metadata as $name => $meta) {
  2785. if (!is_array($meta)) {
  2786. $metadata[$name] = array(self::VALUE => $meta, 'fixity' => self::CONSTANT);
  2787. } elseif (array_key_exists(self::VALUE, $meta)) {
  2788. $metadata[$name]['fixity'] = self::CONSTANT;
  2789. }
  2790. }
  2791. $parts = Strings::split($mask, '/<([^>#= ]+)(=[^># ]*)? *([^>#]*)(#?[^>\[\]]*)>|(\[!?|\]|\s*\?.*)/');
  2792. $this->xlat = array();
  2793. $i = count($parts) - 1;
  2794. if (isset($parts[$i - 1]) && substr(ltrim($parts[$i - 1]), 0, 1) === '?') {
  2795. $matches = Strings::matchAll($parts[$i - 1], '/(?:([a-zA-Z0-9_.-]+)=)?<([^># ]+) *([^>#]*)(#?[^>]*)>/');
  2796. foreach ($matches as $match) {
  2797. list(, $param, $name, $pattern, $class) = $match;
  2798. if ($class !== '') {
  2799. if (!isset(self::$styles[$class])) {
  2800. throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set.");
  2801. }
  2802. $meta = self::$styles[$class];
  2803. } elseif (isset(self::$styles['?' . $name])) {
  2804. $meta = self::$styles['?' . $name];
  2805. } else {
  2806. $meta = self::$styles['?#'];
  2807. }
  2808. if (isset($metadata[$name])) {
  2809. $meta = $metadata[$name] + $meta;
  2810. }
  2811. if (array_key_exists(self::VALUE, $meta)) {
  2812. $meta['fixity'] = self::OPTIONAL;
  2813. }
  2814. unset($meta['pattern']);
  2815. $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]);
  2816. $metadata[$name] = $meta;
  2817. if ($param !== '') {
  2818. $this->xlat[$name] = $param;
  2819. }
  2820. }
  2821. $i -= 6;
  2822. }
  2823. $brackets = 0;
  2824. $re = '';
  2825. $sequence = array();
  2826. $autoOptional = array(0, 0);
  2827. do {
  2828. array_unshift($sequence, $parts[$i]);
  2829. $re = preg_quote($parts[$i], '#') . $re;
  2830. if ($i === 0) {
  2831. break;
  2832. }
  2833. $i--;
  2834. $part = $parts[$i];
  2835. if ($part === '[' || $part === ']' || $part === '[!') {
  2836. $brackets += $part[0] === '[' ? -1 : 1;
  2837. if ($brackets < 0) {
  2838. throw new Nette\InvalidArgumentException("Unexpected '$part' in mask '$mask'.");
  2839. }
  2840. array_unshift($sequence, $part);
  2841. $re = ($part[0] === '[' ? '(?:' : ')?') . $re;
  2842. $i -= 5;
  2843. continue;
  2844. }
  2845. $class = $parts[$i]; $i--;
  2846. $pattern = trim($parts[$i]); $i--;
  2847. $default = $parts[$i]; $i--;
  2848. $name = $parts[$i]; $i--;
  2849. array_unshift($sequence, $name);
  2850. if ($name[0] === '?') {
  2851. $re = '(?:' . preg_quote(substr($name, 1), '#') . '|' . $pattern . ')' . $re;
  2852. $sequence[1] = substr($name, 1) . $sequence[1];
  2853. continue;
  2854. }
  2855. if (preg_match('#[^a-z0-9_-]#i', $name)) {
  2856. throw new Nette\InvalidArgumentException("Parameter name must be alphanumeric string due to limitations of PCRE, '$name' given.");
  2857. }
  2858. if ($class !== '') {
  2859. if (!isset(self::$styles[$class])) {
  2860. throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set.");
  2861. }
  2862. $meta = self::$styles[$class];
  2863. } elseif (isset(self::$styles[$name])) {
  2864. $meta = self::$styles[$name];
  2865. } else {
  2866. $meta = self::$styles['#'];
  2867. }
  2868. if (isset($metadata[$name])) {
  2869. $meta = $metadata[$name] + $meta;
  2870. }
  2871. if ($pattern == '' && isset($meta[self::PATTERN])) {
  2872. $pattern = $meta[self::PATTERN];
  2873. }
  2874. if ($default !== '') {
  2875. $meta[self::VALUE] = (string) substr($default, 1);
  2876. $meta['fixity'] = self::PATH_OPTIONAL;
  2877. }
  2878. $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]);
  2879. if (array_key_exists(self::VALUE, $meta)) {
  2880. if (isset($meta['filterTable2'][$meta[self::VALUE]])) {
  2881. $meta['defOut'] = $meta['filterTable2'][$meta[self::VALUE]];
  2882. } elseif (isset($meta[self::FILTER_OUT])) {
  2883. $meta['defOut'] = call_user_func($meta[self::FILTER_OUT], $meta[self::VALUE]);
  2884. } else {
  2885. $meta['defOut'] = $meta[self::VALUE];
  2886. }
  2887. }
  2888. $meta[self::PATTERN] = "#(?:$pattern)$#A" . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu');
  2889. $re = '(?P<' . str_replace('-', '___', $name) . '>(?U)' . $pattern . ')' . $re;
  2890. if ($brackets) {
  2891. if (!isset($meta[self::VALUE])) {
  2892. $meta[self::VALUE] = $meta['defOut'] = NULL;
  2893. }
  2894. $meta['fixity'] = self::PATH_OPTIONAL;
  2895. } elseif (isset($meta['fixity'])) {
  2896. $re = '(?:' . substr_replace($re, ')?', strlen($re) - $autoOptional[0], 0);
  2897. array_splice($sequence, count($sequence) - $autoOptional[1], 0, array(']', ''));
  2898. array_unshift($sequence, '[', '');
  2899. $meta['fixity'] = self::PATH_OPTIONAL;
  2900. } else {
  2901. $autoOptional = array(strlen($re), count($sequence));
  2902. }
  2903. $metadata[$name] = $meta;
  2904. } while (TRUE);
  2905. if ($brackets) {
  2906. throw new Nette\InvalidArgumentException("Missing closing ']' in mask '$mask'.");
  2907. }
  2908. $this->re = '#' . $re . '/?$#A' . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu');
  2909. $this->metadata = $metadata;
  2910. $this->sequence = $sequence;
  2911. }
  2912. function getMask()
  2913. {
  2914. return $this->mask;
  2915. }
  2916. function getDefaults()
  2917. {
  2918. $defaults = array();
  2919. foreach ($this->metadata as $name => $meta) {
  2920. if (isset($meta['fixity'])) {
  2921. $defaults[$name] = $meta[self::VALUE];
  2922. }
  2923. }
  2924. return $defaults;
  2925. }
  2926. function getTargetPresenter()
  2927. {
  2928. if ($this->flags & self::ONE_WAY) {
  2929. return FALSE;
  2930. }
  2931. $m = $this->metadata;
  2932. $module = '';
  2933. if (isset($m[self::MODULE_KEY])) {
  2934. if (isset($m[self::MODULE_KEY]['fixity']) && $m[self::MODULE_KEY]['fixity'] === self::CONSTANT) {
  2935. $module = $m[self::MODULE_KEY][self::VALUE] . ':';
  2936. } else {
  2937. return NULL;
  2938. }
  2939. }
  2940. if (isset($m[self::PRESENTER_KEY]['fixity']) && $m[self::PRESENTER_KEY]['fixity'] === self::CONSTANT) {
  2941. return $module . $m[self::PRESENTER_KEY][self::VALUE];
  2942. }
  2943. return NULL;
  2944. }
  2945. private static function renameKeys($arr, $xlat)
  2946. {
  2947. if (empty($xlat)) {
  2948. return $arr;
  2949. }
  2950. $res = array();
  2951. $occupied = array_flip($xlat);
  2952. foreach ($arr as $k => $v) {
  2953. if (isset($xlat[$k])) {
  2954. $res[$xlat[$k]] = $v;
  2955. } elseif (!isset($occupied[$k])) {
  2956. $res[$k] = $v;
  2957. }
  2958. }
  2959. return $res;
  2960. }
  2961. private static function action2path($s)
  2962. {
  2963. $s = preg_replace('#(.)(?=[A-Z])#', '$1-', $s);
  2964. $s = strtolower($s);
  2965. $s = rawurlencode($s);
  2966. return $s;
  2967. }
  2968. private static function path2action($s)
  2969. {
  2970. $s = strtolower($s);
  2971. $s = preg_replace('#-(?=[a-z])#', ' ', $s);
  2972. $s = substr(ucwords('x' . $s), 1);
  2973. $s = str_replace(' ', '', $s);
  2974. return $s;
  2975. }
  2976. private static function presenter2path($s)
  2977. {
  2978. $s = strtr($s, ':', '.');
  2979. $s = preg_replace('#([^.])(?=[A-Z])#', '$1-', $s);
  2980. $s = strtolower($s);
  2981. $s = rawurlencode($s);
  2982. return $s;
  2983. }
  2984. private static function path2presenter($s)
  2985. {
  2986. $s = strtolower($s);
  2987. $s = preg_replace('#([.-])(?=[a-z])#', '$1 ', $s);
  2988. $s = ucwords($s);
  2989. $s = str_replace('. ', ':', $s);
  2990. $s = str_replace('- ', '', $s);
  2991. return $s;
  2992. }
  2993. static function addStyle($style, $parent = '#')
  2994. {
  2995. if (isset(self::$styles[$style])) {
  2996. throw new Nette\InvalidArgumentException("Style '$style' already exists.");
  2997. }
  2998. if ($parent !== NULL) {
  2999. if (!isset(self::$styles[$parent])) {
  3000. throw new Nette\InvalidArgumentException("Parent style '$parent' doesn't exist.");
  3001. }
  3002. self::$styles[$style] = self::$styles[$parent];
  3003. } else {
  3004. self::$styles[$style] = array();
  3005. }
  3006. }
  3007. static function setStyleProperty($style, $key, $value)
  3008. {
  3009. if (!isset(self::$styles[$style])) {
  3010. throw new Nette\InvalidArgumentException("Style '$style' doesn't exist.");
  3011. }
  3012. self::$styles[$style][$key] = $value;
  3013. }
  3014. }
  3015. }
  3016. namespace Nette {
  3017. use Nette;
  3018. class ArrayList extends Object implements \ArrayAccess, \Countable, \IteratorAggregate
  3019. {
  3020. private $list = array();
  3021. function getIterator()
  3022. {
  3023. return new \ArrayIterator($this->list);
  3024. }
  3025. function count()
  3026. {
  3027. return count($this->list);
  3028. }
  3029. function offsetSet($index, $value)
  3030. {
  3031. if ($index === NULL) {
  3032. $this->list[] = $value;
  3033. } elseif ($index < 0 || $index >= count($this->list)) {
  3034. throw new OutOfRangeException("Offset invalid or out of range");
  3035. } else {
  3036. $this->list[(int) $index] = $value;
  3037. }
  3038. }
  3039. function offsetGet($index)
  3040. {
  3041. if ($index < 0 || $index >= count($this->list)) {
  3042. throw new OutOfRangeException("Offset invalid or out of range");
  3043. }
  3044. return $this->list[(int) $index];
  3045. }
  3046. function offsetExists($index)
  3047. {
  3048. return $index >= 0 && $index < count($this->list);
  3049. }
  3050. function offsetUnset($index)
  3051. {
  3052. if ($index < 0 || $index >= count($this->list)) {
  3053. throw new OutOfRangeException("Offset invalid or out of range");
  3054. }
  3055. array_splice($this->list, (int) $index, 1);
  3056. }
  3057. }
  3058. }
  3059. namespace Nette\Application\Routers {
  3060. use Nette;
  3061. class RouteList extends Nette\ArrayList implements Nette\Application\IRouter
  3062. {
  3063. private $cachedRoutes;
  3064. private $module;
  3065. function __construct($module = NULL)
  3066. {
  3067. $this->module = $module ? $module . ':' : '';
  3068. }
  3069. function match(Nette\Http\IRequest $httpRequest)
  3070. {
  3071. foreach ($this as $route) {
  3072. $appRequest = $route->match($httpRequest);
  3073. if ($appRequest !== NULL) {
  3074. $appRequest->setPresenterName($this->module . $appRequest->getPresenterName());
  3075. return $appRequest;
  3076. }
  3077. }
  3078. return NULL;
  3079. }
  3080. function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl)
  3081. {
  3082. if ($this->cachedRoutes === NULL) {
  3083. $routes = array();
  3084. $routes['*'] = array();
  3085. foreach ($this as $route) {
  3086. $presenter = $route instanceof Route ? $route->getTargetPresenter() : NULL;
  3087. if ($presenter === FALSE) {
  3088. continue;
  3089. }
  3090. if (is_string($presenter)) {
  3091. $presenter = strtolower($presenter);
  3092. if (!isset($routes[$presenter])) {
  3093. $routes[$presenter] = $routes['*'];
  3094. }
  3095. $routes[$presenter][] = $route;
  3096. } else {
  3097. foreach ($routes as $id => $foo) {
  3098. $routes[$id][] = $route;
  3099. }
  3100. }
  3101. }
  3102. $this->cachedRoutes = $routes;
  3103. }
  3104. if ($this->module) {
  3105. if (strncasecmp($tmp = $appRequest->getPresenterName(), $this->module, strlen($this->module)) === 0) {
  3106. $appRequest = clone $appRequest;
  3107. $appRequest->setPresenterName(substr($tmp, strlen($this->module)));
  3108. } else {
  3109. return NULL;
  3110. }
  3111. }
  3112. $presenter = strtolower($appRequest->getPresenterName());
  3113. if (!isset($this->cachedRoutes[$presenter])) {
  3114. $presenter = '*';
  3115. }
  3116. foreach ($this->cachedRoutes[$presenter] as $route) {
  3117. $url = $route->constructUrl($appRequest, $refUrl);
  3118. if ($url !== NULL) {
  3119. return $url;
  3120. }
  3121. }
  3122. return NULL;
  3123. }
  3124. function offsetSet($index, $route)
  3125. {
  3126. if (!$route instanceof Nette\Application\IRouter) {
  3127. throw new Nette\InvalidArgumentException("Argument must be IRouter descendant.");
  3128. }
  3129. parent::offsetSet($index, $route);
  3130. }
  3131. }
  3132. use Nette\Application;
  3133. class SimpleRouter extends Nette\Object implements Application\IRouter
  3134. {
  3135. const PRESENTER_KEY = 'presenter';
  3136. const MODULE_KEY = 'module';
  3137. private $module = '';
  3138. private $defaults;
  3139. private $flags;
  3140. function __construct($defaults = array(), $flags = 0)
  3141. {
  3142. if (is_string($defaults)) {
  3143. $a = strrpos($defaults, ':');
  3144. if (!$a) {
  3145. throw new Nette\InvalidArgumentException("Argument must be array or string in format Presenter:action, '$defaults' given.");
  3146. }
  3147. $defaults = array(
  3148. self::PRESENTER_KEY => substr($defaults, 0, $a),
  3149. 'action' => $a === strlen($defaults) - 1 ? Application\UI\Presenter::DEFAULT_ACTION : substr($defaults, $a + 1),
  3150. );
  3151. }
  3152. if (isset($defaults[self::MODULE_KEY])) {
  3153. $this->module = $defaults[self::MODULE_KEY] . ':';
  3154. unset($defaults[self::MODULE_KEY]);
  3155. }
  3156. $this->defaults = $defaults;
  3157. $this->flags = $flags;
  3158. }
  3159. function match(Nette\Http\IRequest $httpRequest)
  3160. {
  3161. if ($httpRequest->getUrl()->getPathInfo() !== '') {
  3162. return NULL;
  3163. }
  3164. $params = $httpRequest->getQuery();
  3165. $params += $this->defaults;
  3166. if (!isset($params[self::PRESENTER_KEY])) {
  3167. throw new Nette\InvalidStateException('Missing presenter.');
  3168. }
  3169. $presenter = $this->module . $params[self::PRESENTER_KEY];
  3170. unset($params[self::PRESENTER_KEY]);
  3171. return new Application\Request(
  3172. $presenter,
  3173. $httpRequest->getMethod(),
  3174. $params,
  3175. $httpRequest->getPost(),
  3176. $httpRequest->getFiles(),
  3177. array(Application\Request::SECURED => $httpRequest->isSecured())
  3178. );
  3179. }
  3180. function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl)
  3181. {
  3182. $params = $appRequest->getParams();
  3183. $presenter = $appRequest->getPresenterName();
  3184. if (strncasecmp($presenter, $this->module, strlen($this->module)) === 0) {
  3185. $params[self::PRESENTER_KEY] = substr($presenter, strlen($this->module));
  3186. } else {
  3187. return NULL;
  3188. }
  3189. foreach ($this->defaults as $key => $value) {
  3190. if (isset($params[$key]) && $params[$key] == $value) {
  3191. unset($params[$key]);
  3192. }
  3193. }
  3194. $url = ($this->flags & self::SECURED ? 'https://' : 'http://') . $refUrl->getAuthority() . $refUrl->getPath();
  3195. $sep = ini_get('arg_separator.input');
  3196. $query = http_build_query($params, '', $sep ? $sep[0] : '&');
  3197. if ($query != '') {
  3198. $url .= '?' . $query;
  3199. }
  3200. return $url;
  3201. }
  3202. function getDefaults()
  3203. {
  3204. return $this->defaults;
  3205. }
  3206. }
  3207. }
  3208. namespace Nette\Application\UI {
  3209. use Nette;
  3210. class BadSignalException extends Nette\Application\BadRequestException
  3211. {
  3212. protected $defaultCode = 403;
  3213. }
  3214. }
  3215. namespace Nette\ComponentModel {
  3216. use Nette;
  3217. abstract class Component extends Nette\Object implements IComponent
  3218. {
  3219. private $parent;
  3220. private $name;
  3221. private $monitors = array();
  3222. function __construct(IContainer $parent = NULL, $name = NULL)
  3223. {
  3224. if ($parent !== NULL) {
  3225. $parent->addComponent($this, $name);
  3226. } elseif (is_string($name)) {
  3227. $this->name = $name;
  3228. }
  3229. }
  3230. function lookup($type, $need = TRUE)
  3231. {
  3232. if (!isset($this->monitors[$type])) {
  3233. $obj = $this->parent;
  3234. $path = self::NAME_SEPARATOR . $this->name;
  3235. $depth = 1;
  3236. while ($obj !== NULL) {
  3237. if ($obj instanceof $type) {
  3238. break;
  3239. }
  3240. $path = self::NAME_SEPARATOR . $obj->getName() . $path;
  3241. $depth++;
  3242. $obj = $obj->getParent();
  3243. if ($obj === $this) {
  3244. $obj = NULL;
  3245. }
  3246. }
  3247. if ($obj) {
  3248. $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
  3249. } else {
  3250. $this->monitors[$type] = array(NULL, NULL, NULL, FALSE);
  3251. }
  3252. }
  3253. if ($need && $this->monitors[$type][0] === NULL) {
  3254. throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'.");
  3255. }
  3256. return $this->monitors[$type][0];
  3257. }
  3258. function lookupPath($type, $need = TRUE)
  3259. {
  3260. $this->lookup($type, $need);
  3261. return $this->monitors[$type][2];
  3262. }
  3263. function monitor($type)
  3264. {
  3265. if (empty($this->monitors[$type][3])) {
  3266. if ($obj = $this->lookup($type, FALSE)) {
  3267. $this->attached($obj);
  3268. }
  3269. $this->monitors[$type][3] = TRUE;
  3270. }
  3271. }
  3272. function unmonitor($type)
  3273. {
  3274. unset($this->monitors[$type]);
  3275. }
  3276. protected function attached($obj)
  3277. {
  3278. }
  3279. protected function detached($obj)
  3280. {
  3281. }
  3282. final function getName()
  3283. {
  3284. return $this->name;
  3285. }
  3286. final function getParent()
  3287. {
  3288. return $this->parent;
  3289. }
  3290. function setParent(IContainer $parent = NULL, $name = NULL)
  3291. {
  3292. if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
  3293. $this->name = $name;
  3294. return $this;
  3295. } elseif ($parent === $this->parent && $name === NULL) {
  3296. return $this;
  3297. }
  3298. if ($this->parent !== NULL && $parent !== NULL) {
  3299. throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
  3300. }
  3301. if ($parent === NULL) {
  3302. $this->refreshMonitors(0);
  3303. $this->parent = NULL;
  3304. } else {
  3305. $this->validateParent($parent);
  3306. $this->parent = $parent;
  3307. if ($name !== NULL) {
  3308. $this->name = $name;
  3309. }
  3310. $tmp = array();
  3311. $this->refreshMonitors(0, $tmp);
  3312. }
  3313. return $this;
  3314. }
  3315. protected function validateParent(IContainer $parent)
  3316. {
  3317. }
  3318. private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
  3319. {
  3320. if ($this instanceof IContainer) {
  3321. foreach ($this->getComponents() as $component) {
  3322. if ($component instanceof Component) {
  3323. $component->refreshMonitors($depth + 1, $missing, $listeners);
  3324. }
  3325. }
  3326. }
  3327. if ($missing === NULL) {
  3328. foreach ($this->monitors as $type => $rec) {
  3329. if (isset($rec[1]) && $rec[1] > $depth) {
  3330. if ($rec[3]) {
  3331. $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
  3332. $listeners[] = array($this, $rec[0]);
  3333. } else {
  3334. unset($this->monitors[$type]);
  3335. }
  3336. }
  3337. }
  3338. } else {
  3339. foreach ($this->monitors as $type => $rec) {
  3340. if (isset($rec[0])) {
  3341. continue;
  3342. } elseif (!$rec[3]) {
  3343. unset($this->monitors[$type]);
  3344. } elseif (isset($missing[$type])) {
  3345. $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
  3346. } else {
  3347. $this->monitors[$type] = NULL;
  3348. if ($obj = $this->lookup($type, FALSE)) {
  3349. $listeners[] = array($this, $obj);
  3350. } else {
  3351. $missing[$type] = TRUE;
  3352. }
  3353. $this->monitors[$type][3] = TRUE;
  3354. }
  3355. }
  3356. }
  3357. if ($depth === 0) {
  3358. $method = $missing === NULL ? 'detached' : 'attached';
  3359. foreach ($listeners as $item) {
  3360. $item[0]->$method($item[1]);
  3361. }
  3362. }
  3363. }
  3364. function __clone()
  3365. {
  3366. if ($this->parent === NULL) {
  3367. return;
  3368. } elseif ($this->parent instanceof Container) {
  3369. $this->parent = $this->parent->_isCloning();
  3370. if ($this->parent === NULL) {
  3371. $this->refreshMonitors(0);
  3372. }
  3373. } else {
  3374. $this->parent = NULL;
  3375. $this->refreshMonitors(0);
  3376. }
  3377. }
  3378. final function __wakeup()
  3379. {
  3380. throw new Nette\NotImplementedException;
  3381. }
  3382. }
  3383. class Container extends Component implements IContainer
  3384. {
  3385. private $components = array();
  3386. private $cloning;
  3387. function addComponent(IComponent $component, $name, $insertBefore = NULL)
  3388. {
  3389. if ($name === NULL) {
  3390. $name = $component->getName();
  3391. }
  3392. if (is_int($name)) {
  3393. $name = (string) $name;
  3394. } elseif (!is_string($name)) {
  3395. throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
  3396. } elseif (!preg_match('#^[a-zA-Z0-9_]+$#', $name)) {
  3397. throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given.");
  3398. }
  3399. if (isset($this->components[$name])) {
  3400. throw new Nette\InvalidStateException("Component with name '$name' already exists.");
  3401. }
  3402. $obj = $this;
  3403. do {
  3404. if ($obj === $component) {
  3405. throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'.");
  3406. }
  3407. $obj = $obj->getParent();
  3408. } while ($obj !== NULL);
  3409. $this->validateChildComponent($component);
  3410. try {
  3411. if (isset($this->components[$insertBefore])) {
  3412. $tmp = array();
  3413. foreach ($this->components as $k => $v) {
  3414. if ($k === $insertBefore) {
  3415. $tmp[$name] = $component;
  3416. }
  3417. $tmp[$k] = $v;
  3418. }
  3419. $this->components = $tmp;
  3420. } else {
  3421. $this->components[$name] = $component;
  3422. }
  3423. $component->setParent($this, $name);
  3424. } catch (\Exception $e) {
  3425. unset($this->components[$name]);
  3426. throw $e;
  3427. }
  3428. }
  3429. function removeComponent(IComponent $component)
  3430. {
  3431. $name = $component->getName();
  3432. if (!isset($this->components[$name]) || $this->components[$name] !== $component) {
  3433. throw new Nette\InvalidArgumentException("Component named '$name' is not located in this container.");
  3434. }
  3435. unset($this->components[$name]);
  3436. $component->setParent(NULL);
  3437. }
  3438. final function getComponent($name, $need = TRUE)
  3439. {
  3440. if (is_int($name)) {
  3441. $name = (string) $name;
  3442. } elseif (!is_string($name)) {
  3443. throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
  3444. } else {
  3445. $a = strpos($name, self::NAME_SEPARATOR);
  3446. if ($a !== FALSE) {
  3447. $ext = (string) substr($name, $a + 1);
  3448. $name = substr($name, 0, $a);
  3449. }
  3450. if ($name === '') {
  3451. throw new Nette\InvalidArgumentException("Component or subcomponent name must not be empty string.");
  3452. }
  3453. }
  3454. if (!isset($this->components[$name])) {
  3455. $component = $this->createComponent($name);
  3456. if ($component instanceof IComponent && $component->getParent() === NULL) {
  3457. $this->addComponent($component, $name);
  3458. }
  3459. }
  3460. if (isset($this->components[$name])) {
  3461. if (!isset($ext)) {
  3462. return $this->components[$name];
  3463. } elseif ($this->components[$name] instanceof IContainer) {
  3464. return $this->components[$name]->getComponent($ext, $need);
  3465. } elseif ($need) {
  3466. throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component.");
  3467. }
  3468. } elseif ($need) {
  3469. throw new Nette\InvalidArgumentException("Component with name '$name' does not exist.");
  3470. }
  3471. }
  3472. protected function createComponent($name)
  3473. {
  3474. $ucname = ucfirst($name);
  3475. $method = 'createComponent' . $ucname;
  3476. if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) {
  3477. $component = $this->$method($name);
  3478. if (!$component instanceof IComponent && !isset($this->components[$name])) {
  3479. $class = get_class($this);
  3480. throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
  3481. }
  3482. return $component;
  3483. }
  3484. }
  3485. final function getComponents($deep = FALSE, $filterType = NULL)
  3486. {
  3487. $iterator = new RecursiveComponentIterator($this->components);
  3488. if ($deep) {
  3489. $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
  3490. $iterator = new \RecursiveIteratorIterator($iterator, $deep);
  3491. }
  3492. if ($filterType) {
  3493. $iterator = new Nette\Iterators\InstanceFilter($iterator, $filterType);
  3494. }
  3495. return $iterator;
  3496. }
  3497. protected function validateChildComponent(IComponent $child)
  3498. {
  3499. }
  3500. function __clone()
  3501. {
  3502. if ($this->components) {
  3503. $oldMyself = reset($this->components)->getParent();
  3504. $oldMyself->cloning = $this;
  3505. foreach ($this->components as $name => $component) {
  3506. $this->components[$name] = clone $component;
  3507. }
  3508. $oldMyself->cloning = NULL;
  3509. }
  3510. parent::__clone();
  3511. }
  3512. function _isCloning()
  3513. {
  3514. return $this->cloning;
  3515. }
  3516. }
  3517. }
  3518. namespace Nette\Application\UI {
  3519. use Nette;
  3520. abstract class PresenterComponent extends Nette\ComponentModel\Container implements ISignalReceiver, IStatePersistent, \ArrayAccess
  3521. {
  3522. protected $params = array();
  3523. function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
  3524. {
  3525. $this->monitor('Nette\Application\UI\Presenter');
  3526. parent::__construct($parent, $name);
  3527. }
  3528. function getPresenter($need = TRUE)
  3529. {
  3530. return $this->lookup('Nette\Application\UI\Presenter', $need);
  3531. }
  3532. function getUniqueId()
  3533. {
  3534. return $this->lookupPath('Nette\Application\UI\Presenter', TRUE);
  3535. }
  3536. protected function attached($presenter)
  3537. {
  3538. if ($presenter instanceof Presenter) {
  3539. $this->loadState($presenter->popGlobalParams($this->getUniqueId()));
  3540. }
  3541. }
  3542. protected function tryCall($method, array $params)
  3543. {
  3544. $rc = $this->getReflection();
  3545. if ($rc->hasMethod($method)) {
  3546. $rm = $rc->getMethod($method);
  3547. if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) {
  3548. $this->checkRequirements($rm);
  3549. $rm->invokeNamedArgs($this, $params);
  3550. return TRUE;
  3551. }
  3552. }
  3553. return FALSE;
  3554. }
  3555. function checkRequirements($element)
  3556. {
  3557. }
  3558. static function getReflection()
  3559. {
  3560. return new PresenterComponentReflection(get_called_class());
  3561. }
  3562. function loadState(array $params)
  3563. {
  3564. foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) {
  3565. if (isset($params[$nm])) {
  3566. if (isset($meta['def'])) {
  3567. if (is_array($params[$nm]) && !is_array($meta['def'])) {
  3568. $params[$nm] = $meta['def'];
  3569. } else {
  3570. settype($params[$nm], gettype($meta['def']));
  3571. }
  3572. }
  3573. $this->$nm = & $params[$nm];
  3574. }
  3575. }
  3576. $this->params = $params;
  3577. }
  3578. function saveState(array & $params, $reflection = NULL)
  3579. {
  3580. $reflection = $reflection === NULL ? $this->getReflection() : $reflection;
  3581. foreach ($reflection->getPersistentParams() as $nm => $meta) {
  3582. if (isset($params[$nm])) {
  3583. $val = $params[$nm];
  3584. } elseif (array_key_exists($nm, $params)) {
  3585. continue;
  3586. } elseif (!isset($meta['since']) || $this instanceof $meta['since']) {
  3587. $val = $this->$nm;
  3588. } else {
  3589. continue;
  3590. }
  3591. if (is_object($val)) {
  3592. $class = get_class($this);
  3593. throw new Nette\InvalidStateException("Persistent parameter must be scalar or array, $class::\$$nm is " . gettype($val));
  3594. } else {
  3595. if (isset($meta['def'])) {
  3596. settype($val, gettype($meta['def']));
  3597. if ($val === $meta['def']) {
  3598. $val = NULL;
  3599. }
  3600. } else {
  3601. if ((string) $val === '') {
  3602. $val = NULL;
  3603. }
  3604. }
  3605. $params[$nm] = $val;
  3606. }
  3607. }
  3608. }
  3609. final function getParam($name = NULL, $default = NULL)
  3610. {
  3611. if (func_num_args() === 0) {
  3612. return $this->params;
  3613. } elseif (isset($this->params[$name])) {
  3614. return $this->params[$name];
  3615. } else {
  3616. return $default;
  3617. }
  3618. }
  3619. final function getParamId($name)
  3620. {
  3621. $uid = $this->getUniqueId();
  3622. return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name;
  3623. }
  3624. static function getPersistentParams()
  3625. {
  3626. $rc = new Nette\Reflection\ClassType(get_called_class());
  3627. $params = array();
  3628. foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) {
  3629. if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) {
  3630. $params[] = $rp->getName();
  3631. }
  3632. }
  3633. return $params;
  3634. }
  3635. function signalReceived($signal)
  3636. {
  3637. if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) {
  3638. $class = get_class($this);
  3639. throw new BadSignalException("There is no handler for signal '$signal' in class $class.");
  3640. }
  3641. }
  3642. function formatSignalMethod($signal)
  3643. {
  3644. return $signal == NULL ? NULL : 'handle' . $signal;
  3645. }
  3646. function link($destination, $args = array())
  3647. {
  3648. if (!is_array($args)) {
  3649. $args = func_get_args();
  3650. array_shift($args);
  3651. }
  3652. try {
  3653. return $this->getPresenter()->createRequest($this, $destination, $args, 'link');
  3654. } catch (InvalidLinkException $e) {
  3655. return $this->getPresenter()->handleInvalidLink($e);
  3656. }
  3657. }
  3658. function lazyLink($destination, $args = array())
  3659. {
  3660. if (!is_array($args)) {
  3661. $args = func_get_args();
  3662. array_shift($args);
  3663. }
  3664. return new Link($this, $destination, $args);
  3665. }
  3666. function isLinkCurrent($destination = NULL, $args = array())
  3667. {
  3668. if ($destination !== NULL) {
  3669. if (!is_array($args)) {
  3670. $args = func_get_args();
  3671. array_shift($args);
  3672. }
  3673. $this->link($destination, $args);
  3674. }
  3675. return $this->getPresenter()->getLastCreatedRequestFlag('current');
  3676. }
  3677. function redirect($code, $destination = NULL, $args = array())
  3678. {
  3679. if (!is_numeric($code)) {
  3680. $args = $destination;
  3681. $destination = $code;
  3682. $code = NULL;
  3683. }
  3684. if (!is_array($args)) {
  3685. $args = func_get_args();
  3686. if (is_numeric(array_shift($args))) {
  3687. array_shift($args);
  3688. }
  3689. }
  3690. $presenter = $this->getPresenter();
  3691. $presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code);
  3692. }
  3693. final function offsetSet($name, $component)
  3694. {
  3695. $this->addComponent($component, $name);
  3696. }
  3697. final function offsetGet($name)
  3698. {
  3699. return $this->getComponent($name, TRUE);
  3700. }
  3701. final function offsetExists($name)
  3702. {
  3703. return $this->getComponent($name, FALSE) !== NULL;
  3704. }
  3705. final function offsetUnset($name)
  3706. {
  3707. $component = $this->getComponent($name, FALSE);
  3708. if ($component !== NULL) {
  3709. $this->removeComponent($component);
  3710. }
  3711. }
  3712. }
  3713. abstract class Control extends PresenterComponent implements IPartiallyRenderable
  3714. {
  3715. private $template;
  3716. private $invalidSnippets = array();
  3717. public $snippetMode;
  3718. final function getTemplate()
  3719. {
  3720. if ($this->template === NULL) {
  3721. $value = $this->createTemplate();
  3722. if (!$value instanceof Nette\Templating\ITemplate && $value !== NULL) {
  3723. $class2 = get_class($value); $class = get_class($this);
  3724. throw new Nette\UnexpectedValueException("Object returned by $class::createTemplate() must be instance of Nette\\Templating\\ITemplate, '$class2' given.");
  3725. }
  3726. $this->template = $value;
  3727. }
  3728. return $this->template;
  3729. }
  3730. protected function createTemplate($class = NULL)
  3731. {
  3732. $template = $class ? new $class : new Nette\Templating\FileTemplate;
  3733. $presenter = $this->getPresenter(FALSE);
  3734. $template->onPrepareFilters[] = callback($this, 'templatePrepareFilters');
  3735. $template->registerHelperLoader('Nette\Templating\DefaultHelpers::loader');
  3736. $template->control = $this;
  3737. $template->presenter = $presenter;
  3738. if ($presenter instanceof Presenter) {
  3739. $template->setCacheStorage($presenter->getContext()->templateCacheStorage);
  3740. $template->user = $presenter->getUser();
  3741. $template->netteHttpResponse = $presenter->getHttpResponse();
  3742. $template->netteCacheStorage = $presenter->getContext()->cacheStorage;
  3743. $template->baseUri = $template->baseUrl = rtrim($presenter->getHttpRequest()->getUrl()->getBaseUrl(), '/');
  3744. $template->basePath = preg_replace('#https?://[^/]+#A', '', $template->baseUrl);
  3745. if ($presenter->hasFlashSession()) {
  3746. $id = $this->getParamId('flash');
  3747. $template->flashes = $presenter->getFlashSession()->$id;
  3748. }
  3749. }
  3750. if (!isset($template->flashes) || !is_array($template->flashes)) {
  3751. $template->flashes = array();
  3752. }
  3753. return $template;
  3754. }
  3755. function templatePrepareFilters($template)
  3756. {
  3757. $template->registerFilter(new Nette\Latte\Engine);
  3758. }
  3759. function getWidget($name)
  3760. {
  3761. return $this->getComponent($name);
  3762. }
  3763. function flashMessage($message, $type = 'info')
  3764. {
  3765. $id = $this->getParamId('flash');
  3766. $messages = $this->getPresenter()->getFlashSession()->$id;
  3767. $messages[] = $flash = (object) array(
  3768. 'message' => $message,
  3769. 'type' => $type,
  3770. );
  3771. $this->getTemplate()->flashes = $messages;
  3772. $this->getPresenter()->getFlashSession()->$id = $messages;
  3773. return $flash;
  3774. }
  3775. function invalidateControl($snippet = NULL)
  3776. {
  3777. $this->invalidSnippets[$snippet] = TRUE;
  3778. }
  3779. function validateControl($snippet = NULL)
  3780. {
  3781. if ($snippet === NULL) {
  3782. $this->invalidSnippets = array();
  3783. } else {
  3784. unset($this->invalidSnippets[$snippet]);
  3785. }
  3786. }
  3787. function isControlInvalid($snippet = NULL)
  3788. {
  3789. if ($snippet === NULL) {
  3790. if (count($this->invalidSnippets) > 0) {
  3791. return TRUE;
  3792. } else {
  3793. foreach ($this->getComponents() as $component) {
  3794. if ($component instanceof IRenderable && $component->isControlInvalid()) {
  3795. return TRUE;
  3796. }
  3797. }
  3798. return FALSE;
  3799. }
  3800. } else {
  3801. return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]);
  3802. }
  3803. }
  3804. function getSnippetId($name = NULL)
  3805. {
  3806. return 'snippet-' . $this->getUniqueId() . '-' . $name;
  3807. }
  3808. }
  3809. }
  3810. namespace Nette\Forms {
  3811. use Nette;
  3812. class Container extends Nette\ComponentModel\Container implements \ArrayAccess
  3813. {
  3814. public $onValidate;
  3815. protected $currentGroup;
  3816. protected $valid;
  3817. function setDefaults($values, $erase = FALSE)
  3818. {
  3819. $form = $this->getForm(FALSE);
  3820. if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
  3821. $this->setValues($values, $erase);
  3822. }
  3823. return $this;
  3824. }
  3825. function setValues($values, $erase = FALSE)
  3826. {
  3827. if ($values instanceof \Traversable) {
  3828. $values = iterator_to_array($values);
  3829. } elseif (!is_array($values)) {
  3830. throw new Nette\InvalidArgumentException("First parameter must be an array, " . gettype($values) ." given.");
  3831. }
  3832. foreach ($this->getComponents() as $name => $control) {
  3833. if ($control instanceof IControl) {
  3834. if (array_key_exists($name, $values)) {
  3835. $control->setValue($values[$name]);
  3836. } elseif ($erase) {
  3837. $control->setValue(NULL);
  3838. }
  3839. } elseif ($control instanceof Container) {
  3840. if (array_key_exists($name, $values)) {
  3841. $control->setValues($values[$name], $erase);
  3842. } elseif ($erase) {
  3843. $control->setValues(array(), $erase);
  3844. }
  3845. }
  3846. }
  3847. return $this;
  3848. }
  3849. function getValues()
  3850. {
  3851. $values = new Nette\ArrayHash;
  3852. foreach ($this->getComponents() as $name => $control) {
  3853. if ($control instanceof IControl && !$control->isDisabled() && !$control instanceof ISubmitterControl) {
  3854. $values->$name = $control->getValue();
  3855. } elseif ($control instanceof Container) {
  3856. $values->$name = $control->getValues();
  3857. }
  3858. }
  3859. return $values;
  3860. }
  3861. function isValid()
  3862. {
  3863. if ($this->valid === NULL) {
  3864. $this->validate();
  3865. }
  3866. return $this->valid;
  3867. }
  3868. function validate()
  3869. {
  3870. $this->valid = TRUE;
  3871. $this->onValidate($this);
  3872. foreach ($this->getControls() as $control) {
  3873. if (!$control->getRules()->validate()) {
  3874. $this->valid = FALSE;
  3875. }
  3876. }
  3877. }
  3878. function setCurrentGroup(ControlGroup $group = NULL)
  3879. {
  3880. $this->currentGroup = $group;
  3881. return $this;
  3882. }
  3883. function getCurrentGroup()
  3884. {
  3885. return $this->currentGroup;
  3886. }
  3887. function addComponent(Nette\ComponentModel\IComponent $component, $name, $insertBefore = NULL)
  3888. {
  3889. parent::addComponent($component, $name, $insertBefore);
  3890. if ($this->currentGroup !== NULL && $component instanceof IControl) {
  3891. $this->currentGroup->add($component);
  3892. }
  3893. }
  3894. function getControls()
  3895. {
  3896. return $this->getComponents(TRUE, 'Nette\Forms\IControl');
  3897. }
  3898. function getForm($need = TRUE)
  3899. {
  3900. return $this->lookup('Nette\Forms\Form', $need);
  3901. }
  3902. function addText($name, $label = NULL, $cols = NULL, $maxLength = NULL)
  3903. {
  3904. return $this[$name] = new Controls\TextInput($label, $cols, $maxLength);
  3905. }
  3906. function addPassword($name, $label = NULL, $cols = NULL, $maxLength = NULL)
  3907. {
  3908. $control = new Controls\TextInput($label, $cols, $maxLength);
  3909. $control->setType('password');
  3910. return $this[$name] = $control;
  3911. }
  3912. function addTextArea($name, $label = NULL, $cols = 40, $rows = 10)
  3913. {
  3914. return $this[$name] = new Controls\TextArea($label, $cols, $rows);
  3915. }
  3916. function addUpload($name, $label = NULL)
  3917. {
  3918. return $this[$name] = new Controls\UploadControl($label);
  3919. }
  3920. function addHidden($name, $default = NULL)
  3921. {
  3922. $control = new Controls\HiddenField;
  3923. $control->setDefaultValue($default);
  3924. return $this[$name] = $control;
  3925. }
  3926. function addCheckbox($name, $caption = NULL)
  3927. {
  3928. return $this[$name] = new Controls\Checkbox($caption);
  3929. }
  3930. function addRadioList($name, $label = NULL, array $items = NULL)
  3931. {
  3932. return $this[$name] = new Controls\RadioList($label, $items);
  3933. }
  3934. function addSelect($name, $label = NULL, array $items = NULL, $size = NULL)
  3935. {
  3936. return $this[$name] = new Controls\SelectBox($label, $items, $size);
  3937. }
  3938. function addMultiSelect($name, $label = NULL, array $items = NULL, $size = NULL)
  3939. {
  3940. return $this[$name] = new Controls\MultiSelectBox($label, $items, $size);
  3941. }
  3942. function addSubmit($name, $caption = NULL)
  3943. {
  3944. return $this[$name] = new Controls\SubmitButton($caption);
  3945. }
  3946. function addButton($name, $caption)
  3947. {
  3948. return $this[$name] = new Controls\Button($caption);
  3949. }
  3950. function addImage($name, $src = NULL, $alt = NULL)
  3951. {
  3952. return $this[$name] = new Controls\ImageButton($src, $alt);
  3953. }
  3954. function addContainer($name)
  3955. {
  3956. $control = new Container;
  3957. $control->currentGroup = $this->currentGroup;
  3958. return $this[$name] = $control;
  3959. }
  3960. final function offsetSet($name, $component)
  3961. {
  3962. $this->addComponent($component, $name);
  3963. }
  3964. final function offsetGet($name)
  3965. {
  3966. return $this->getComponent($name, TRUE);
  3967. }
  3968. final function offsetExists($name)
  3969. {
  3970. return $this->getComponent($name, FALSE) !== NULL;
  3971. }
  3972. final function offsetUnset($name)
  3973. {
  3974. $component = $this->getComponent($name, FALSE);
  3975. if ($component !== NULL) {
  3976. $this->removeComponent($component);
  3977. }
  3978. }
  3979. final function __clone()
  3980. {
  3981. throw new Nette\NotImplementedException('Form cloning is not supported yet.');
  3982. }
  3983. function addFile($name, $label = NULL)
  3984. {
  3985. trigger_error(__METHOD__ . '() is deprecated; use addUpload() instead.', E_USER_WARNING);
  3986. return $this->addUpload($name, $label);
  3987. }
  3988. }
  3989. class Form extends Container
  3990. {
  3991. const EQUAL = ':equal',
  3992. IS_IN = ':equal',
  3993. FILLED = ':filled',
  3994. VALID = ':valid';
  3995. const PROTECTION = 'Nette\Forms\Controls\HiddenField::validateEqual';
  3996. const SUBMITTED = ':submitted';
  3997. const MIN_LENGTH = ':minLength',
  3998. MAX_LENGTH = ':maxLength',
  3999. LENGTH = ':length',
  4000. EMAIL = ':email',
  4001. URL = ':url',
  4002. REGEXP = ':regexp',
  4003. PATTERN = ':pattern',
  4004. INTEGER = ':integer',
  4005. NUMERIC = ':integer',
  4006. FLOAT = ':float',
  4007. RANGE = ':range';
  4008. const MAX_FILE_SIZE = ':fileSize',
  4009. MIME_TYPE = ':mimeType',
  4010. IMAGE = ':image';
  4011. const GET = 'get',
  4012. POST = 'post';
  4013. const TRACKER_ID = '_form_';
  4014. const PROTECTOR_ID = '_token_';
  4015. public $onSuccess;
  4016. public $onError;
  4017. public $onSubmit;
  4018. public $onInvalidSubmit;
  4019. private $submittedBy;
  4020. private $httpData;
  4021. private $element;
  4022. private $renderer;
  4023. private $translator;
  4024. private $groups = array();
  4025. private $errors = array();
  4026. function __construct($name = NULL)
  4027. {
  4028. $this->element = Nette\Utils\Html::el('form');
  4029. $this->element->action = '';
  4030. $this->element->method = self::POST;
  4031. $this->element->id = 'frm-' . $name;
  4032. $this->monitor(__CLASS__);
  4033. if ($name !== NULL) {
  4034. $tracker = new Controls\HiddenField($name);
  4035. $tracker->unmonitor(__CLASS__);
  4036. $this[self::TRACKER_ID] = $tracker;
  4037. }
  4038. parent::__construct(NULL, $name);
  4039. }
  4040. protected function attached($obj)
  4041. {
  4042. if ($obj instanceof self) {
  4043. throw new Nette\InvalidStateException('Nested forms are forbidden.');
  4044. }
  4045. }
  4046. final function getForm($need = TRUE)
  4047. {
  4048. return $this;
  4049. }
  4050. function setAction($url)
  4051. {
  4052. $this->element->action = $url;
  4053. return $this;
  4054. }
  4055. function getAction()
  4056. {
  4057. return $this->element->action;
  4058. }
  4059. function setMethod($method)
  4060. {
  4061. if ($this->httpData !== NULL) {
  4062. throw new Nette\InvalidStateException(__METHOD__ . '() must be called until the form is empty.');
  4063. }
  4064. $this->element->method = strtolower($method);
  4065. return $this;
  4066. }
  4067. function getMethod()
  4068. {
  4069. return $this->element->method;
  4070. }
  4071. function addProtection($message = NULL, $timeout = NULL)
  4072. {
  4073. $session = $this->getSession()->getSection('Nette.Forms.Form/CSRF');
  4074. $key = "key$timeout";
  4075. if (isset($session->$key)) {
  4076. $token = $session->$key;
  4077. } else {
  4078. $session->$key = $token = Nette\Utils\Strings::random();
  4079. }
  4080. $session->setExpiration($timeout, $key);
  4081. $this[self::PROTECTOR_ID] = new Controls\HiddenField($token);
  4082. $this[self::PROTECTOR_ID]->addRule(self::PROTECTION, $message, $token);
  4083. }
  4084. function addGroup($caption = NULL, $setAsCurrent = TRUE)
  4085. {
  4086. $group = new ControlGroup;
  4087. $group->setOption('label', $caption);
  4088. $group->setOption('visual', TRUE);
  4089. if ($setAsCurrent) {
  4090. $this->setCurrentGroup($group);
  4091. }
  4092. if (isset($this->groups[$caption])) {
  4093. return $this->groups[] = $group;
  4094. } else {
  4095. return $this->groups[$caption] = $group;
  4096. }
  4097. }
  4098. function removeGroup($name)
  4099. {
  4100. if (is_string($name) && isset($this->groups[$name])) {
  4101. $group = $this->groups[$name];
  4102. } elseif ($name instanceof ControlGroup && in_array($name, $this->groups, TRUE)) {
  4103. $group = $name;
  4104. $name = array_search($group, $this->groups, TRUE);
  4105. } else {
  4106. throw new Nette\InvalidArgumentException("Group not found in form '$this->name'");
  4107. }
  4108. foreach ($group->getControls() as $control) {
  4109. $this->removeComponent($control);
  4110. }
  4111. unset($this->groups[$name]);
  4112. }
  4113. function getGroups()
  4114. {
  4115. return $this->groups;
  4116. }
  4117. function getGroup($name)
  4118. {
  4119. return isset($this->groups[$name]) ? $this->groups[$name] : NULL;
  4120. }
  4121. function setTranslator(Nette\Localization\ITranslator $translator = NULL)
  4122. {
  4123. $this->translator = $translator;
  4124. return $this;
  4125. }
  4126. final function getTranslator()
  4127. {
  4128. return $this->translator;
  4129. }
  4130. function isAnchored()
  4131. {
  4132. return TRUE;
  4133. }
  4134. final function isSubmitted()
  4135. {
  4136. if ($this->submittedBy === NULL) {
  4137. $this->getHttpData();
  4138. $this->submittedBy = !empty($this->httpData);
  4139. }
  4140. return $this->submittedBy;
  4141. }
  4142. function setSubmittedBy(ISubmitterControl $by = NULL)
  4143. {
  4144. $this->submittedBy = $by === NULL ? FALSE : $by;
  4145. return $this;
  4146. }
  4147. final function getHttpData()
  4148. {
  4149. if ($this->httpData === NULL) {
  4150. if (!$this->isAnchored()) {
  4151. throw new Nette\InvalidStateException('Form is not anchored and therefore can not determine whether it was submitted.');
  4152. }
  4153. $this->httpData = (array) $this->receiveHttpData();
  4154. }
  4155. return $this->httpData;
  4156. }
  4157. function fireEvents()
  4158. {
  4159. if (!$this->isSubmitted()) {
  4160. return;
  4161. } elseif ($this->submittedBy instanceof ISubmitterControl) {
  4162. if (!$this->submittedBy->getValidationScope() || $this->isValid()) {
  4163. $this->submittedBy->click();
  4164. } else {
  4165. $this->submittedBy->onInvalidClick($this->submittedBy);
  4166. }
  4167. }
  4168. if ($this->isValid()) {
  4169. $this->onSuccess($this);
  4170. } else {
  4171. $this->onError($this);
  4172. if ($this->onInvalidSubmit) {
  4173. trigger_error(__CLASS__ . '->onInvalidSubmit is deprecated; use onError instead.', E_USER_WARNING);
  4174. $this->onInvalidSubmit($this);
  4175. }
  4176. }
  4177. if ($this->onSuccess) {
  4178. $this->onSubmit($this);
  4179. } elseif ($this->onSubmit) {
  4180. trigger_error(__CLASS__ . '->onSubmit changed its behavior; use onSuccess instead.', E_USER_WARNING);
  4181. if ($this->isValid()) {
  4182. $this->onSubmit($this);
  4183. }
  4184. }
  4185. }
  4186. protected function receiveHttpData()
  4187. {
  4188. $httpRequest = $this->getHttpRequest();
  4189. if (strcasecmp($this->getMethod(), $httpRequest->getMethod())) {
  4190. return;
  4191. }
  4192. if ($httpRequest->isMethod('post')) {
  4193. $data = Nette\Utils\Arrays::mergeTree($httpRequest->getPost(), $httpRequest->getFiles());
  4194. } else {
  4195. $data = $httpRequest->getQuery();
  4196. }
  4197. if ($tracker = $this->getComponent(self::TRACKER_ID, FALSE)) {
  4198. if (!isset($data[self::TRACKER_ID]) || $data[self::TRACKER_ID] !== $tracker->getValue()) {
  4199. return;
  4200. }
  4201. }
  4202. return $data;
  4203. }
  4204. function getValues()
  4205. {
  4206. $values = parent::getValues();
  4207. unset($values[self::TRACKER_ID], $values[self::PROTECTOR_ID]);
  4208. return $values;
  4209. }
  4210. function addError($message)
  4211. {
  4212. $this->valid = FALSE;
  4213. if ($message !== NULL && !in_array($message, $this->errors, TRUE)) {
  4214. $this->errors[] = $message;
  4215. }
  4216. }
  4217. function getErrors()
  4218. {
  4219. return $this->errors;
  4220. }
  4221. function hasErrors()
  4222. {
  4223. return (bool) $this->getErrors();
  4224. }
  4225. function cleanErrors()
  4226. {
  4227. $this->errors = array();
  4228. $this->valid = NULL;
  4229. }
  4230. function getElementPrototype()
  4231. {
  4232. return $this->element;
  4233. }
  4234. function setRenderer(IFormRenderer $renderer)
  4235. {
  4236. $this->renderer = $renderer;
  4237. return $this;
  4238. }
  4239. final function getRenderer()
  4240. {
  4241. if ($this->renderer === NULL) {
  4242. $this->renderer = new Rendering\DefaultFormRenderer;
  4243. }
  4244. return $this->renderer;
  4245. }
  4246. function render()
  4247. {
  4248. $args = func_get_args();
  4249. array_unshift($args, $this);
  4250. echo call_user_func_array(array($this->getRenderer(), 'render'), $args);
  4251. }
  4252. function __toString()
  4253. {
  4254. try {
  4255. return $this->getRenderer()->render($this);
  4256. } catch (\Exception $e) {
  4257. if (func_get_args() && func_get_arg(0)) {
  4258. throw $e;
  4259. } else {
  4260. Nette\Diagnostics\Debugger::toStringException($e);
  4261. }
  4262. }
  4263. }
  4264. protected function getHttpRequest()
  4265. {
  4266. return Nette\Environment::getHttpRequest();
  4267. }
  4268. protected function getSession()
  4269. {
  4270. return Nette\Environment::getSession();
  4271. }
  4272. }
  4273. }
  4274. namespace Nette\Application\UI {
  4275. use Nette;
  4276. class Form extends Nette\Forms\Form implements ISignalReceiver
  4277. {
  4278. function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
  4279. {
  4280. parent::__construct();
  4281. $this->monitor('Nette\Application\UI\Presenter');
  4282. if ($parent !== NULL) {
  4283. $parent->addComponent($this, $name);
  4284. }
  4285. }
  4286. function getPresenter($need = TRUE)
  4287. {
  4288. return $this->lookup('Nette\Application\UI\Presenter', $need);
  4289. }
  4290. protected function attached($presenter)
  4291. {
  4292. if ($presenter instanceof Presenter) {
  4293. $name = $this->lookupPath('Nette\Application\UI\Presenter');
  4294. if (!isset($this->getElementPrototype()->id)) {
  4295. $this->getElementPrototype()->id = 'frm-' . $name;
  4296. }
  4297. $this->setAction(new Link(
  4298. $presenter,
  4299. $name . self::NAME_SEPARATOR . 'submit!',
  4300. array()
  4301. ));
  4302. if ($this->isSubmitted()) {
  4303. foreach ($this->getControls() as $control) {
  4304. $control->loadHttpData();
  4305. }
  4306. }
  4307. }
  4308. parent::attached($presenter);
  4309. }
  4310. function isAnchored()
  4311. {
  4312. return (bool) $this->getPresenter(FALSE);
  4313. }
  4314. protected function receiveHttpData()
  4315. {
  4316. $presenter = $this->getPresenter();
  4317. if (!$presenter->isSignalReceiver($this, 'submit')) {
  4318. return;
  4319. }
  4320. $isPost = $this->getMethod() === self::POST;
  4321. $request = $presenter->getRequest();
  4322. if ($request->isMethod('forward') || $request->isMethod('post') !== $isPost) {
  4323. return;
  4324. }
  4325. if ($isPost) {
  4326. return Nette\Utils\Arrays::mergeTree($request->getPost(), $request->getFiles());
  4327. } else {
  4328. return $request->getParams();
  4329. }
  4330. }
  4331. function signalReceived($signal)
  4332. {
  4333. if ($signal === 'submit') {
  4334. $this->fireEvents();
  4335. } else {
  4336. $class = get_class($this);
  4337. throw new BadSignalException("Missing handler for signal '$signal' in $class.");
  4338. }
  4339. }
  4340. }
  4341. class InvalidLinkException extends \Exception
  4342. {
  4343. }
  4344. class Link extends Nette\Object
  4345. {
  4346. private $component;
  4347. private $destination;
  4348. private $params;
  4349. function __construct(PresenterComponent $component, $destination, array $params)
  4350. {
  4351. $this->component = $component;
  4352. $this->destination = $destination;
  4353. $this->params = $params;
  4354. }
  4355. function getDestination()
  4356. {
  4357. return $this->destination;
  4358. }
  4359. function setParam($key, $value)
  4360. {
  4361. $this->params[$key] = $value;
  4362. return $this;
  4363. }
  4364. function getParam($key)
  4365. {
  4366. return isset($this->params[$key]) ? $this->params[$key] : NULL;
  4367. }
  4368. function getParams()
  4369. {
  4370. return $this->params;
  4371. }
  4372. function __toString()
  4373. {
  4374. try {
  4375. return $this->component->link($this->destination, $this->params);
  4376. } catch (\Exception $e) {
  4377. Nette\Diagnostics\Debugger::toStringException($e);
  4378. }
  4379. }
  4380. }
  4381. use Nette\Application;use Nette\Application\Responses;use Nette\Http;use Nette\Reflection;
  4382. abstract class Presenter extends Control implements Application\IPresenter
  4383. {
  4384. const INVALID_LINK_SILENT = 1,
  4385. INVALID_LINK_WARNING = 2,
  4386. INVALID_LINK_EXCEPTION = 3;
  4387. const SIGNAL_KEY = 'do',
  4388. ACTION_KEY = 'action',
  4389. FLASH_KEY = '_fid',
  4390. DEFAULT_ACTION = 'default';
  4391. public static $invalidLinkMode;
  4392. public $onShutdown;
  4393. private $request;
  4394. private $response;
  4395. public $autoCanonicalize = TRUE;
  4396. public $absoluteUrls = FALSE;
  4397. private $globalParams;
  4398. private $globalState;
  4399. private $globalStateSinces;
  4400. private $action;
  4401. private $view;
  4402. private $layout;
  4403. private $payload;
  4404. private $signalReceiver;
  4405. private $signal;
  4406. private $ajaxMode;
  4407. private $startupCheck;
  4408. private $lastCreatedRequest;
  4409. private $lastCreatedRequestFlag;
  4410. private $context;
  4411. final function getRequest()
  4412. {
  4413. return $this->request;
  4414. }
  4415. final function getPresenter($need = TRUE)
  4416. {
  4417. return $this;
  4418. }
  4419. final function getUniqueId()
  4420. {
  4421. return '';
  4422. }
  4423. function run(Application\Request $request)
  4424. {
  4425. try {
  4426. $this->request = $request;
  4427. $this->payload = (object) NULL;
  4428. $this->setParent($this->getParent(), $request->getPresenterName());
  4429. $this->initGlobalParams();
  4430. $this->checkRequirements($this->getReflection());
  4431. $this->startup();
  4432. if (!$this->startupCheck) {
  4433. $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName();
  4434. throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup().");
  4435. }
  4436. $this->tryCall($this->formatActionMethod($this->getAction()), $this->params);
  4437. if ($this->autoCanonicalize) {
  4438. $this->canonicalize();
  4439. }
  4440. if ($this->getHttpRequest()->isMethod('head')) {
  4441. $this->terminate();
  4442. }
  4443. $this->processSignal();
  4444. $this->beforeRender();
  4445. $this->tryCall($this->formatRenderMethod($this->getView()), $this->params);
  4446. $this->afterRender();
  4447. $this->saveGlobalState();
  4448. if ($this->isAjax()) {
  4449. $this->payload->state = $this->getGlobalState();
  4450. }
  4451. $this->sendTemplate();
  4452. } catch (Application\AbortException $e) {
  4453. if ($this->isAjax()) try {
  4454. $hasPayload = (array) $this->payload; unset($hasPayload['state']);
  4455. if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) {
  4456. $this->snippetMode = TRUE;
  4457. $this->response->send($this->getHttpRequest(), $this->getHttpResponse());
  4458. $this->sendPayload();
  4459. } elseif (!$this->response && $hasPayload) {
  4460. $this->sendPayload();
  4461. }
  4462. } catch (Application\AbortException $e) { }
  4463. if ($this->hasFlashSession()) {
  4464. $this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds': '+ 3 seconds');
  4465. }
  4466. $this->onShutdown($this, $this->response);
  4467. $this->shutdown($this->response);
  4468. return $this->response;
  4469. }
  4470. }
  4471. protected function startup()
  4472. {
  4473. $this->startupCheck = TRUE;
  4474. }
  4475. protected function beforeRender()
  4476. {
  4477. }
  4478. protected function afterRender()
  4479. {
  4480. }
  4481. protected function shutdown($response)
  4482. {
  4483. }
  4484. function checkRequirements($element)
  4485. {
  4486. $user = (array) $element->getAnnotation('User');
  4487. if (in_array('loggedIn', $user) && !$this->getUser()->isLoggedIn()) {
  4488. throw new Application\ForbiddenRequestException;
  4489. }
  4490. }
  4491. function processSignal()
  4492. {
  4493. if ($this->signal === NULL) {
  4494. return;
  4495. }
  4496. $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE);
  4497. if ($component === NULL) {
  4498. throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found.");
  4499. } elseif (!$component instanceof ISignalReceiver) {
  4500. throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor.");
  4501. }
  4502. $component->signalReceived($this->signal);
  4503. $this->signal = NULL;
  4504. }
  4505. final function getSignal()
  4506. {
  4507. return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal);
  4508. }
  4509. final function isSignalReceiver($component, $signal = NULL)
  4510. {
  4511. if ($component instanceof Nette\ComponentModel\Component) {
  4512. $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE);
  4513. }
  4514. if ($this->signal === NULL) {
  4515. return FALSE;
  4516. } elseif ($signal === TRUE) {
  4517. return $component === ''
  4518. || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0;
  4519. } elseif ($signal === NULL) {
  4520. return $this->signalReceiver === $component;
  4521. } else {
  4522. return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0;
  4523. }
  4524. }
  4525. final function getAction($fullyQualified = FALSE)
  4526. {
  4527. return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action;
  4528. }
  4529. function changeAction($action)
  4530. {
  4531. if (Nette\Utils\Strings::match($action, "#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*$#")) {
  4532. $this->action = $action;
  4533. $this->view = $action;
  4534. } else {
  4535. throw new Application\BadRequestException("Action name '$action' is not alphanumeric string.");
  4536. }
  4537. }
  4538. final function getView()
  4539. {
  4540. return $this->view;
  4541. }
  4542. function setView($view)
  4543. {
  4544. $this->view = (string) $view;
  4545. return $this;
  4546. }
  4547. final function getLayout()
  4548. {
  4549. return $this->layout;
  4550. }
  4551. function setLayout($layout)
  4552. {
  4553. $this->layout = $layout === FALSE ? FALSE : (string) $layout;
  4554. return $this;
  4555. }
  4556. function sendTemplate()
  4557. {
  4558. $template = $this->getTemplate();
  4559. if (!$template) {
  4560. return;
  4561. }
  4562. if ($template instanceof Nette\Templating\IFileTemplate && !$template->getFile()) {
  4563. $files = $this->formatTemplateFiles();
  4564. foreach ($files as $file) {
  4565. if (is_file($file)) {
  4566. $template->setFile($file);
  4567. break;
  4568. }
  4569. }
  4570. if (!$template->getFile()) {
  4571. $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files));
  4572. $file = strtr($file, '/', DIRECTORY_SEPARATOR);
  4573. throw new Application\BadRequestException("Page not found. Missing template '$file'.");
  4574. }
  4575. }
  4576. if ($this->layout !== FALSE) {
  4577. $files = $this->formatLayoutTemplateFiles();
  4578. foreach ($files as $file) {
  4579. if (is_file($file)) {
  4580. $template->layout = $file;
  4581. $template->_extends = $file;
  4582. break;
  4583. }
  4584. }
  4585. if (empty($template->layout) && $this->layout !== NULL) {
  4586. $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files));
  4587. $file = strtr($file, '/', DIRECTORY_SEPARATOR);
  4588. throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'.");
  4589. }
  4590. }
  4591. $this->sendResponse(new Responses\TextResponse($template));
  4592. }
  4593. function formatLayoutTemplateFiles()
  4594. {
  4595. $name = $this->getName();
  4596. $presenter = substr($name, strrpos(':' . $name, ':'));
  4597. $layout = $this->layout ? $this->layout : 'layout';
  4598. $dir = dirname(dirname($this->getReflection()->getFileName()));
  4599. $list = array(
  4600. "$dir/templates/$presenter/@$layout.latte",
  4601. "$dir/templates/$presenter.@$layout.latte",
  4602. "$dir/templates/$presenter/@$layout.phtml",
  4603. "$dir/templates/$presenter.@$layout.phtml",
  4604. );
  4605. do {
  4606. $list[] = "$dir/templates/@$layout.latte";
  4607. $list[] = "$dir/templates/@$layout.phtml";
  4608. $dir = dirname($dir);
  4609. } while ($dir && ($name = substr($name, 0, strrpos($name, ':'))));
  4610. return $list;
  4611. }
  4612. function formatTemplateFiles()
  4613. {
  4614. $name = $this->getName();
  4615. $presenter = substr($name, strrpos(':' . $name, ':'));
  4616. $dir = dirname(dirname($this->getReflection()->getFileName()));
  4617. return array(
  4618. "$dir/templates/$presenter/$this->view.latte",
  4619. "$dir/templates/$presenter.$this->view.latte",
  4620. "$dir/templates/$presenter/$this->view.phtml",
  4621. "$dir/templates/$presenter.$this->view.phtml",
  4622. );
  4623. }
  4624. protected static function formatActionMethod($action)
  4625. {
  4626. return 'action' . $action;
  4627. }
  4628. protected static function formatRenderMethod($view)
  4629. {
  4630. return 'render' . $view;
  4631. }
  4632. final function getPayload()
  4633. {
  4634. return $this->payload;
  4635. }
  4636. function isAjax()
  4637. {
  4638. if ($this->ajaxMode === NULL) {
  4639. $this->ajaxMode = $this->getHttpRequest()->isAjax();
  4640. }
  4641. return $this->ajaxMode;
  4642. }
  4643. function sendPayload()
  4644. {
  4645. $this->sendResponse(new Responses\JsonResponse($this->payload));
  4646. }
  4647. function sendResponse(Application\IResponse $response)
  4648. {
  4649. $this->response = $response;
  4650. $this->terminate();
  4651. }
  4652. function terminate()
  4653. {
  4654. if (func_num_args() !== 0) {
  4655. trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING);
  4656. $this->sendResponse(func_get_arg(0));
  4657. }
  4658. throw new Application\AbortException();
  4659. }
  4660. function forward($destination, $args = array())
  4661. {
  4662. if ($destination instanceof Application\Request) {
  4663. $this->sendResponse(new Responses\ForwardResponse($destination));
  4664. } elseif (!is_array($args)) {
  4665. $args = func_get_args();
  4666. array_shift($args);
  4667. }
  4668. $this->createRequest($this, $destination, $args, 'forward');
  4669. $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest));
  4670. }
  4671. function redirectUrl($url, $code = NULL)
  4672. {
  4673. if ($this->isAjax()) {
  4674. $this->payload->redirect = (string) $url;
  4675. $this->sendPayload();
  4676. } elseif (!$code) {
  4677. $code = $this->getHttpRequest()->isMethod('post')
  4678. ? Http\IResponse::S303_POST_GET
  4679. : Http\IResponse::S302_FOUND;
  4680. }
  4681. $this->sendResponse(new Responses\RedirectResponse($url, $code));
  4682. }
  4683. function redirectUri($url, $code = NULL)
  4684. {
  4685. trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING);
  4686. $this->redirectUrl($url, $code);
  4687. }
  4688. function backlink()
  4689. {
  4690. return $this->getAction(TRUE);
  4691. }
  4692. function getLastCreatedRequest()
  4693. {
  4694. return $this->lastCreatedRequest;
  4695. }
  4696. function getLastCreatedRequestFlag($flag)
  4697. {
  4698. return !empty($this->lastCreatedRequestFlag[$flag]);
  4699. }
  4700. function canonicalize()
  4701. {
  4702. if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) {
  4703. $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->params, 'redirectX');
  4704. if ($url !== NULL && !$this->getHttpRequest()->getUrl()->isEqual($url)) {
  4705. $this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY));
  4706. }
  4707. }
  4708. }
  4709. function lastModified($lastModified, $etag = NULL, $expire = NULL)
  4710. {
  4711. if ($expire !== NULL) {
  4712. $this->getHttpResponse()->setExpiration($expire);
  4713. }
  4714. if (!$this->getHttpContext()->isModified($lastModified, $etag)) {
  4715. $this->terminate();
  4716. }
  4717. }
  4718. final protected function createRequest($component, $destination, array $args, $mode)
  4719. {
  4720. static $presenterFactory, $router, $refUrl;
  4721. if ($presenterFactory === NULL) {
  4722. $presenterFactory = $this->getApplication()->getPresenterFactory();
  4723. $router = $this->getApplication()->getRouter();
  4724. $refUrl = new Http\Url($this->getHttpRequest()->getUrl());
  4725. $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath());
  4726. }
  4727. $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
  4728. $a = strpos($destination, '#');
  4729. if ($a === FALSE) {
  4730. $fragment = '';
  4731. } else {
  4732. $fragment = substr($destination, $a);
  4733. $destination = substr($destination, 0, $a);
  4734. }
  4735. $a = strpos($destination, '?');
  4736. if ($a !== FALSE) {
  4737. parse_str(substr($destination, $a + 1), $args);
  4738. $destination = substr($destination, 0, $a);
  4739. }
  4740. $a = strpos($destination, '//');
  4741. if ($a === FALSE) {
  4742. $scheme = FALSE;
  4743. } else {
  4744. $scheme = substr($destination, 0, $a);
  4745. $destination = substr($destination, $a + 2);
  4746. }
  4747. if (!$component instanceof Presenter || substr($destination, -1) === '!') {
  4748. $signal = rtrim($destination, '!');
  4749. $a = strrpos($signal, ':');
  4750. if ($a !== FALSE) {
  4751. $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
  4752. $signal = (string) substr($signal, $a + 1);
  4753. }
  4754. if ($signal == NULL) {
  4755. throw new InvalidLinkException("Signal must be non-empty string.");
  4756. }
  4757. $destination = 'this';
  4758. }
  4759. if ($destination == NULL) {
  4760. throw new InvalidLinkException("Destination must be non-empty string.");
  4761. }
  4762. $current = FALSE;
  4763. $a = strrpos($destination, ':');
  4764. if ($a === FALSE) {
  4765. $action = $destination === 'this' ? $this->action : $destination;
  4766. $presenter = $this->getName();
  4767. $presenterClass = get_class($this);
  4768. } else {
  4769. $action = (string) substr($destination, $a + 1);
  4770. if ($destination[0] === ':') {
  4771. if ($a < 2) {
  4772. throw new InvalidLinkException("Missing presenter name in '$destination'.");
  4773. }
  4774. $presenter = substr($destination, 1, $a - 1);
  4775. } else {
  4776. $presenter = $this->getName();
  4777. $b = strrpos($presenter, ':');
  4778. if ($b === FALSE) {
  4779. $presenter = substr($destination, 0, $a);
  4780. } else {
  4781. $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
  4782. }
  4783. }
  4784. try {
  4785. $presenterClass = $presenterFactory->getPresenterClass($presenter);
  4786. } catch (Application\InvalidPresenterException $e) {
  4787. throw new InvalidLinkException($e->getMessage(), NULL, $e);
  4788. }
  4789. }
  4790. if (isset($signal)) {
  4791. $reflection = new PresenterComponentReflection(get_class($component));
  4792. if ($signal === 'this') {
  4793. $signal = '';
  4794. if (array_key_exists(0, $args)) {
  4795. throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
  4796. }
  4797. } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) {
  4798. $method = $component->formatSignalMethod($signal);
  4799. if (!$reflection->hasCallableMethod($method)) {
  4800. throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
  4801. }
  4802. if ($args) {
  4803. self::argsToParams(get_class($component), $method, $args);
  4804. }
  4805. }
  4806. if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
  4807. $component->saveState($args);
  4808. }
  4809. if ($args && $component !== $this) {
  4810. $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
  4811. foreach ($args as $key => $val) {
  4812. unset($args[$key]);
  4813. $args[$prefix . $key] = $val;
  4814. }
  4815. }
  4816. }
  4817. if (is_subclass_of($presenterClass, __CLASS__)) {
  4818. if ($action === '') {
  4819. $action = self::DEFAULT_ACTION;
  4820. }
  4821. $current = ($action === '*' || $action === $this->action) && $presenterClass === get_class($this);
  4822. $reflection = new PresenterComponentReflection($presenterClass);
  4823. if ($args || $destination === 'this') {
  4824. $method = $presenterClass::formatActionMethod($action);
  4825. if (!$reflection->hasCallableMethod($method)) {
  4826. $method = $presenterClass::formatRenderMethod($action);
  4827. if (!$reflection->hasCallableMethod($method)) {
  4828. $method = NULL;
  4829. }
  4830. }
  4831. if ($method === NULL) {
  4832. if (array_key_exists(0, $args)) {
  4833. throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
  4834. }
  4835. } elseif ($destination === 'this') {
  4836. self::argsToParams($presenterClass, $method, $args, $this->params);
  4837. } else {
  4838. self::argsToParams($presenterClass, $method, $args);
  4839. }
  4840. }
  4841. if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
  4842. $this->saveState($args, $reflection);
  4843. }
  4844. $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
  4845. if ($current && $args) {
  4846. $tmp = $globalState + $this->params;
  4847. foreach ($args as $key => $val) {
  4848. if ((string) $val !== (isset($tmp[$key]) ? (string) $tmp[$key] : '')) {
  4849. $current = FALSE;
  4850. break;
  4851. }
  4852. }
  4853. }
  4854. $args += $globalState;
  4855. }
  4856. $args[self::ACTION_KEY] = $action;
  4857. if (!empty($signal)) {
  4858. $args[self::SIGNAL_KEY] = $component->getParamId($signal);
  4859. $current = $current && $args[self::SIGNAL_KEY] === $this->getParam(self::SIGNAL_KEY);
  4860. }
  4861. if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
  4862. $args[self::FLASH_KEY] = $this->getParam(self::FLASH_KEY);
  4863. }
  4864. $this->lastCreatedRequest = new Application\Request(
  4865. $presenter,
  4866. Application\Request::FORWARD,
  4867. $args,
  4868. array(),
  4869. array()
  4870. );
  4871. $this->lastCreatedRequestFlag = array('current' => $current);
  4872. if ($mode === 'forward') {
  4873. return;
  4874. }
  4875. $url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
  4876. if ($url === NULL) {
  4877. unset($args[self::ACTION_KEY]);
  4878. $params = urldecode(http_build_query($args, NULL, ', '));
  4879. throw new InvalidLinkException("No route for $presenter:$action($params)");
  4880. }
  4881. if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
  4882. $hostUrl = $refUrl->getHostUrl();
  4883. if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
  4884. $url = substr($url, strlen($hostUrl));
  4885. }
  4886. }
  4887. return $url . $fragment;
  4888. }
  4889. private static function argsToParams($class, $method, & $args, $supplemental = array())
  4890. {
  4891. static $cache;
  4892. $params = & $cache[strtolower($class . ':' . $method)];
  4893. if ($params === NULL) {
  4894. $params = Reflection\Method::from($class, $method)->getDefaultParameters();
  4895. }
  4896. $i = 0;
  4897. foreach ($params as $name => $def) {
  4898. if (array_key_exists($i, $args)) {
  4899. $args[$name] = $args[$i];
  4900. unset($args[$i]);
  4901. $i++;
  4902. } elseif (array_key_exists($name, $args)) {
  4903. } elseif (array_key_exists($name, $supplemental)) {
  4904. $args[$name] = $supplemental[$name];
  4905. } else {
  4906. continue;
  4907. }
  4908. if ($def === NULL) {
  4909. if ((string) $args[$name] === '') {
  4910. $args[$name] = NULL;
  4911. }
  4912. } else {
  4913. settype($args[$name], gettype($def));
  4914. if ($args[$name] === $def) {
  4915. $args[$name] = NULL;
  4916. }
  4917. }
  4918. }
  4919. if (array_key_exists($i, $args)) {
  4920. $method = Reflection\Method::from($class, $method)->getName();
  4921. throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
  4922. }
  4923. }
  4924. protected function handleInvalidLink($e)
  4925. {
  4926. if (self::$invalidLinkMode === self::INVALID_LINK_SILENT) {
  4927. return '#';
  4928. } elseif (self::$invalidLinkMode === self::INVALID_LINK_WARNING) {
  4929. return 'error: ' . $e->getMessage();
  4930. } else {
  4931. throw $e;
  4932. }
  4933. }
  4934. static function getPersistentComponents()
  4935. {
  4936. return (array) Reflection\ClassType::from(get_called_class())
  4937. ->getAnnotation('persistent');
  4938. }
  4939. private function getGlobalState($forClass = NULL)
  4940. {
  4941. $sinces = & $this->globalStateSinces;
  4942. if ($this->globalState === NULL) {
  4943. $state = array();
  4944. foreach ($this->globalParams as $id => $params) {
  4945. $prefix = $id . self::NAME_SEPARATOR;
  4946. foreach ($params as $key => $val) {
  4947. $state[$prefix . $key] = $val;
  4948. }
  4949. }
  4950. $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
  4951. if ($sinces === NULL) {
  4952. $sinces = array();
  4953. foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) {
  4954. $sinces[$nm] = $meta['since'];
  4955. }
  4956. }
  4957. $components = $this->getReflection()->getPersistentComponents();
  4958. $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent');
  4959. foreach ($iterator as $name => $component) {
  4960. if ($iterator->getDepth() === 0) {
  4961. $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
  4962. }
  4963. $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
  4964. $params = array();
  4965. $component->saveState($params);
  4966. foreach ($params as $key => $val) {
  4967. $state[$prefix . $key] = $val;
  4968. $sinces[$prefix . $key] = $since;
  4969. }
  4970. }
  4971. } else {
  4972. $state = $this->globalState;
  4973. }
  4974. if ($forClass !== NULL) {
  4975. $since = NULL;
  4976. foreach ($state as $key => $foo) {
  4977. if (!isset($sinces[$key])) {
  4978. $x = strpos($key, self::NAME_SEPARATOR);
  4979. $x = $x === FALSE ? $key : substr($key, 0, $x);
  4980. $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
  4981. }
  4982. if ($since !== $sinces[$key]) {
  4983. $since = $sinces[$key];
  4984. $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
  4985. }
  4986. if (!$ok) {
  4987. unset($state[$key]);
  4988. }
  4989. }
  4990. }
  4991. return $state;
  4992. }
  4993. protected function saveGlobalState()
  4994. {
  4995. foreach ($this->globalParams as $id => $foo) {
  4996. $this->getComponent($id, FALSE);
  4997. }
  4998. $this->globalParams = array();
  4999. $this->globalState = $this->getGlobalState();
  5000. }
  5001. private function initGlobalParams()
  5002. {
  5003. $this->globalParams = array();
  5004. $selfParams = array();
  5005. $params = $this->request->getParams();
  5006. if ($this->isAjax()) {
  5007. $params = $this->request->getPost() + $params;
  5008. }
  5009. foreach ($params as $key => $value) {
  5010. $a = strlen($key) > 2 ? strrpos($key, self::NAME_SEPARATOR, -2) : FALSE;
  5011. if ($a === FALSE) {
  5012. $selfParams[$key] = $value;
  5013. } else {
  5014. $this->globalParams[substr($key, 0, $a)][substr($key, $a + 1)] = $value;
  5015. }
  5016. }
  5017. $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
  5018. $this->signalReceiver = $this->getUniqueId();
  5019. if (!empty($selfParams[self::SIGNAL_KEY])) {
  5020. $param = $selfParams[self::SIGNAL_KEY];
  5021. $pos = strrpos($param, '-');
  5022. if ($pos) {
  5023. $this->signalReceiver = substr($param, 0, $pos);
  5024. $this->signal = substr($param, $pos + 1);
  5025. } else {
  5026. $this->signalReceiver = $this->getUniqueId();
  5027. $this->signal = $param;
  5028. }
  5029. if ($this->signal == NULL) {
  5030. $this->signal = NULL;
  5031. }
  5032. }
  5033. $this->loadState($selfParams);
  5034. }
  5035. final function popGlobalParams($id)
  5036. {
  5037. if (isset($this->globalParams[$id])) {
  5038. $res = $this->globalParams[$id];
  5039. unset($this->globalParams[$id]);
  5040. return $res;
  5041. } else {
  5042. return array();
  5043. }
  5044. }
  5045. function hasFlashSession()
  5046. {
  5047. return !empty($this->params[self::FLASH_KEY])
  5048. && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
  5049. }
  5050. function getFlashSession()
  5051. {
  5052. if (empty($this->params[self::FLASH_KEY])) {
  5053. $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4);
  5054. }
  5055. return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
  5056. }
  5057. function setContext(Nette\DI\IContainer $context)
  5058. {
  5059. $this->context = $context;
  5060. return $this;
  5061. }
  5062. final function getContext()
  5063. {
  5064. return $this->context;
  5065. }
  5066. final function getService($name)
  5067. {
  5068. return $this->context->getService($name);
  5069. }
  5070. protected function getHttpRequest()
  5071. {
  5072. return $this->context->httpRequest;
  5073. }
  5074. protected function getHttpResponse()
  5075. {
  5076. return $this->context->httpResponse;
  5077. }
  5078. protected function getHttpContext()
  5079. {
  5080. return $this->context->httpContext;
  5081. }
  5082. function getApplication()
  5083. {
  5084. return $this->context->application;
  5085. }
  5086. function getSession($namespace = NULL)
  5087. {
  5088. $handler = $this->context->session;
  5089. return $namespace === NULL ? $handler : $handler->getSection($namespace);
  5090. }
  5091. function getUser()
  5092. {
  5093. return $this->context->user;
  5094. }
  5095. }
  5096. }
  5097. namespace Nette\Reflection {
  5098. use Nette;use Nette\ObjectMixin;
  5099. class ClassType extends \ReflectionClass
  5100. {
  5101. private static $extMethods;
  5102. static function from($class)
  5103. {
  5104. return new static($class);
  5105. }
  5106. function __toString()
  5107. {
  5108. return 'Class ' . $this->getName();
  5109. }
  5110. function hasEventProperty($name)
  5111. {
  5112. if (preg_match('#^on[A-Z]#', $name) && $this->hasProperty($name)) {
  5113. $rp = $this->getProperty($name);
  5114. return $rp->isPublic() && !$rp->isStatic();
  5115. }
  5116. return FALSE;
  5117. }
  5118. function setExtensionMethod($name, $callback)
  5119. {
  5120. $l = & self::$extMethods[strtolower($name)];
  5121. $l[strtolower($this->getName())] = callback($callback);
  5122. $l[''] = NULL;
  5123. return $this;
  5124. }
  5125. function getExtensionMethod($name)
  5126. {
  5127. $class = strtolower($this->getName());
  5128. $l = & self::$extMethods[strtolower($name)];
  5129. if (empty($l)) {
  5130. return FALSE;
  5131. } elseif (isset($l[''][$class])) {
  5132. return $l[''][$class];
  5133. }
  5134. $cl = $class;
  5135. do {
  5136. if (isset($l[$cl])) {
  5137. return $l[''][$class] = $l[$cl];
  5138. }
  5139. } while (($cl = strtolower(get_parent_class($cl))) !== '');
  5140. foreach (class_implements($class) as $cl) {
  5141. $cl = strtolower($cl);
  5142. if (isset($l[$cl])) {
  5143. return $l[''][$class] = $l[$cl];
  5144. }
  5145. }
  5146. return $l[''][$class] = FALSE;
  5147. }
  5148. function getConstructor()
  5149. {
  5150. return ($ref = parent::getConstructor()) ? Method::from($this->getName(), $ref->getName()) : NULL;
  5151. }
  5152. function getExtension()
  5153. {
  5154. return ($name = $this->getExtensionName()) ? new Extension($name) : NULL;
  5155. }
  5156. function getInterfaces()
  5157. {
  5158. $res = array();
  5159. foreach (parent::getInterfaceNames() as $val) {
  5160. $res[$val] = new static($val);
  5161. }
  5162. return $res;
  5163. }
  5164. function getMethod($name)
  5165. {
  5166. return new Method($this->getName(), $name);
  5167. }
  5168. function getMethods($filter = -1)
  5169. {
  5170. foreach ($res = parent::getMethods($filter) as $key => $val) {
  5171. $res[$key] = new Method($this->getName(), $val->getName());
  5172. }
  5173. return $res;
  5174. }
  5175. function getParentClass()
  5176. {
  5177. return ($ref = parent::getParentClass()) ? new static($ref->getName()) : NULL;
  5178. }
  5179. function getProperties($filter = -1)
  5180. {
  5181. foreach ($res = parent::getProperties($filter) as $key => $val) {
  5182. $res[$key] = new Property($this->getName(), $val->getName());
  5183. }
  5184. return $res;
  5185. }
  5186. function getProperty($name)
  5187. {
  5188. return new Property($this->getName(), $name);
  5189. }
  5190. function hasAnnotation($name)
  5191. {
  5192. $res = AnnotationsParser::getAll($this);
  5193. return !empty($res[$name]);
  5194. }
  5195. function getAnnotation($name)
  5196. {
  5197. $res = AnnotationsParser::getAll($this);
  5198. return isset($res[$name]) ? end($res[$name]) : NULL;
  5199. }
  5200. function getAnnotations()
  5201. {
  5202. return AnnotationsParser::getAll($this);
  5203. }
  5204. function getDescription()
  5205. {
  5206. return $this->getAnnotation('description');
  5207. }
  5208. static function getReflection()
  5209. {
  5210. return new ClassType(get_called_class());
  5211. }
  5212. function __call($name, $args)
  5213. {
  5214. return ObjectMixin::call($this, $name, $args);
  5215. }
  5216. function &__get($name)
  5217. {
  5218. return ObjectMixin::get($this, $name);
  5219. }
  5220. function __set($name, $value)
  5221. {
  5222. return ObjectMixin::set($this, $name, $value);
  5223. }
  5224. function __isset($name)
  5225. {
  5226. return ObjectMixin::has($this, $name);
  5227. }
  5228. function __unset($name)
  5229. {
  5230. ObjectMixin::remove($this, $name);
  5231. }
  5232. }
  5233. }
  5234. namespace Nette\Application\UI {
  5235. use Nette;
  5236. class PresenterComponentReflection extends Nette\Reflection\ClassType
  5237. {
  5238. private static $ppCache = array();
  5239. private static $pcCache = array();
  5240. private static $mcCache = array();
  5241. function getPersistentParams($class = NULL)
  5242. {
  5243. $class = $class === NULL ? $this->getName() : $class;
  5244. $params = & self::$ppCache[$class];
  5245. if ($params !== NULL) {
  5246. return $params;
  5247. }
  5248. $params = array();
  5249. if (is_subclass_of($class, 'Nette\Application\UI\PresenterComponent')) {
  5250. $defaults = get_class_vars($class);
  5251. foreach (call_user_func(array($class, 'getPersistentParams'), $class) as $name => $meta) {
  5252. if (is_string($meta)) {
  5253. $name = $meta;
  5254. }
  5255. $params[$name] = array(
  5256. 'def' => $defaults[$name],
  5257. 'since' => $class,
  5258. );
  5259. }
  5260. foreach ($this->getPersistentParams(get_parent_class($class)) as $name => $param) {
  5261. if (isset($params[$name])) {
  5262. $params[$name]['since'] = $param['since'];
  5263. continue;
  5264. }
  5265. $params[$name] = $param;
  5266. }
  5267. }
  5268. return $params;
  5269. }
  5270. function getPersistentComponents()
  5271. {
  5272. $class = $this->getName();
  5273. $components = & self::$pcCache[$class];
  5274. if ($components !== NULL) {
  5275. return $components;
  5276. }
  5277. $components = array();
  5278. if (is_subclass_of($class, 'Nette\Application\UI\Presenter')) {
  5279. foreach (call_user_func(array($class, 'getPersistentComponents'), $class) as $name => $meta) {
  5280. if (is_string($meta)) {
  5281. $name = $meta;
  5282. }
  5283. $components[$name] = array('since' => $class);
  5284. }
  5285. $components = self::getPersistentComponents(get_parent_class($class)) + $components;
  5286. }
  5287. return $components;
  5288. }
  5289. function hasCallableMethod($method)
  5290. {
  5291. $class = $this->getName();
  5292. $cache = & self::$mcCache[strtolower($class . ':' . $method)];
  5293. if ($cache === NULL) try {
  5294. $cache = FALSE;
  5295. $rm = Nette\Reflection\Method::from($class, $method);
  5296. $cache = $this->isInstantiable() && $rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic();
  5297. } catch (\ReflectionException $e) {
  5298. }
  5299. return $cache;
  5300. }
  5301. }
  5302. }
  5303. namespace Nette\Caching {
  5304. use Nette;
  5305. class Cache extends Nette\Object implements \ArrayAccess
  5306. {
  5307. const PRIORITY = 'priority',
  5308. EXPIRATION = 'expire',
  5309. EXPIRE = 'expire',
  5310. SLIDING = 'sliding',
  5311. TAGS = 'tags',
  5312. FILES = 'files',
  5313. ITEMS = 'items',
  5314. CONSTS = 'consts',
  5315. CALLBACKS = 'callbacks',
  5316. ALL = 'all';
  5317. const NAMESPACE_SEPARATOR = "\x00";
  5318. private $storage;
  5319. private $namespace;
  5320. private $key;
  5321. private $data;
  5322. function __construct(IStorage $storage, $namespace = NULL)
  5323. {
  5324. $this->storage = $storage;
  5325. $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
  5326. }
  5327. function getStorage()
  5328. {
  5329. return $this->storage;
  5330. }
  5331. function getNamespace()
  5332. {
  5333. return (string) substr($this->namespace, 0, -1);
  5334. }
  5335. function derive($namespace)
  5336. {
  5337. $derived = new static($this->storage, $this->namespace . $namespace);
  5338. return $derived;
  5339. }
  5340. function release()
  5341. {
  5342. $this->key = $this->data = NULL;
  5343. }
  5344. function load($key)
  5345. {
  5346. $key = is_scalar($key) ? (string) $key : serialize($key);
  5347. if ($this->key === $key) {
  5348. return $this->data;
  5349. }
  5350. $this->key = $key;
  5351. $this->data = $this->storage->read($this->namespace . md5($key));
  5352. return $this->data;
  5353. }
  5354. function save($key, $data, array $dp = NULL)
  5355. {
  5356. $this->key = is_scalar($key) ? (string) $key : serialize($key);
  5357. $key = $this->namespace . md5($this->key);
  5358. if (isset($dp[Cache::EXPIRATION])) {
  5359. $dp[Cache::EXPIRATION] = Nette\DateTime::from($dp[Cache::EXPIRATION])->format('U') - time();
  5360. }
  5361. if (isset($dp[self::FILES])) {
  5362. foreach ((array) $dp[self::FILES] as $item) {
  5363. $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item));
  5364. }
  5365. unset($dp[self::FILES]);
  5366. }
  5367. if (isset($dp[self::ITEMS])) {
  5368. $dp[self::ITEMS] = (array) $dp[self::ITEMS];
  5369. foreach ($dp[self::ITEMS] as $k => $item) {
  5370. $dp[self::ITEMS][$k] = $this->namespace . md5(is_scalar($item) ? $item : serialize($item));
  5371. }
  5372. }
  5373. if (isset($dp[self::CONSTS])) {
  5374. foreach ((array) $dp[self::CONSTS] as $item) {
  5375. $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
  5376. }
  5377. unset($dp[self::CONSTS]);
  5378. }
  5379. if ($data instanceof Nette\Callback || $data instanceof \Closure) {
  5380. Nette\Utils\CriticalSection::enter();
  5381. $data = $data->__invoke();
  5382. Nette\Utils\CriticalSection::leave();
  5383. }
  5384. if (is_object($data)) {
  5385. $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
  5386. Nette\Reflection\ClassType::from($data)->getAnnotation('serializationVersion'));
  5387. }
  5388. $this->data = $data;
  5389. if ($data === NULL) {
  5390. $this->storage->remove($key);
  5391. } else {
  5392. $this->storage->write($key, $data, (array) $dp);
  5393. }
  5394. return $data;
  5395. }
  5396. function clean(array $conds = NULL)
  5397. {
  5398. $this->release();
  5399. $this->storage->clean((array) $conds);
  5400. }
  5401. function call($function)
  5402. {
  5403. $key = func_get_args();
  5404. if ($this->load($key) === NULL) {
  5405. array_shift($key);
  5406. return $this->save($this->key, call_user_func_array($function, $key));
  5407. } else {
  5408. return $this->data;
  5409. }
  5410. }
  5411. function start($key)
  5412. {
  5413. if ($this->offsetGet($key) === NULL) {
  5414. return new OutputHelper($this, $key);
  5415. } else {
  5416. echo $this->data;
  5417. }
  5418. }
  5419. function offsetSet($key, $data)
  5420. {
  5421. $this->save($key, $data);
  5422. }
  5423. function offsetGet($key)
  5424. {
  5425. return $this->load($key);
  5426. }
  5427. function offsetExists($key)
  5428. {
  5429. return $this->load($key) !== NULL;
  5430. }
  5431. function offsetUnset($key)
  5432. {
  5433. $this->save($key, NULL);
  5434. }
  5435. static function checkCallbacks($callbacks)
  5436. {
  5437. foreach ($callbacks as $callback) {
  5438. $func = array_shift($callback);
  5439. if (!call_user_func_array($func, $callback)) {
  5440. return FALSE;
  5441. }
  5442. }
  5443. return TRUE;
  5444. }
  5445. private static function checkConst($const, $value)
  5446. {
  5447. return defined($const) && constant($const) === $value;
  5448. }
  5449. private static function checkFile($file, $time)
  5450. {
  5451. return @filemtime($file) == $time;
  5452. }
  5453. private static function checkSerializationVersion($class, $value)
  5454. {
  5455. return Nette\Reflection\ClassType::from($class)->getAnnotation('serializationVersion') === $value;
  5456. }
  5457. }
  5458. class OutputHelper extends Nette\Object
  5459. {
  5460. public $dependencies;
  5461. private $cache;
  5462. private $key;
  5463. function __construct(Cache $cache, $key)
  5464. {
  5465. $this->cache = $cache;
  5466. $this->key = $key;
  5467. ob_start();
  5468. }
  5469. function end()
  5470. {
  5471. if ($this->cache === NULL) {
  5472. throw new Nette\InvalidStateException('Output cache has already been saved.');
  5473. }
  5474. $this->cache->save($this->key, ob_get_flush(), $this->dependencies);
  5475. $this->cache = NULL;
  5476. }
  5477. }
  5478. }
  5479. namespace Nette\Caching\Storages {
  5480. use Nette;
  5481. class DevNullStorage extends Nette\Object implements Nette\Caching\IStorage
  5482. {
  5483. function read($key)
  5484. {
  5485. }
  5486. function write($key, $data, array $dp)
  5487. {
  5488. }
  5489. function remove($key)
  5490. {
  5491. }
  5492. function clean(array $conds)
  5493. {
  5494. }
  5495. }
  5496. use Nette\Caching\Cache;
  5497. class FileJournal extends Nette\Object implements IJournal
  5498. {
  5499. const FILE = 'btfj.dat';
  5500. const FILE_MAGIC = 0x6274666A;
  5501. const INDEX_MAGIC = 0x696E6465;
  5502. const DATA_MAGIC = 0x64617461;
  5503. const NODE_SIZE = 4096;
  5504. const BITROT = 12;
  5505. const HEADER_SIZE = 4096;
  5506. const INT32_SIZE = 4;
  5507. const INFO = 'i',
  5508. TYPE = 't',
  5509. IS_LEAF = 'il',
  5510. PREV_NODE = 'p',
  5511. END = 'e',
  5512. MAX = 'm',
  5513. INDEX_DATA = 'id',
  5514. LAST_INDEX = 'l';
  5515. const TAGS = 't',
  5516. PRIORITY = 'p',
  5517. ENTRIES = 'e';
  5518. const DATA = 'd',
  5519. KEY = 'k',
  5520. DELETED = 'd';
  5521. public static $debug = FALSE;
  5522. private $file;
  5523. private $handle;
  5524. private $lastNode = 2;
  5525. private $lastModTime = NULL;
  5526. private $nodeCache = array();
  5527. private $nodeChanged = array();
  5528. private $toCommit = array();
  5529. private $deletedLinks = array();
  5530. private $dataNodeFreeSpace = array();
  5531. private static $startNode = array(
  5532. self::TAGS => 0,
  5533. self::PRIORITY => 1,
  5534. self::ENTRIES => 2,
  5535. self::DATA => 3,
  5536. );
  5537. function __construct($dir)
  5538. {
  5539. $this->file = $dir . '/' . self::FILE;
  5540. if (!file_exists($this->file)) {
  5541. $init = @fopen($this->file, 'xb');
  5542. if (!$init) {
  5543. clearstatcache();
  5544. if (!file_exists($this->file)) {
  5545. throw new Nette\InvalidStateException("Cannot create journal file $this->file.");
  5546. }
  5547. } else {
  5548. $writen = fwrite($init, pack('N2', self::FILE_MAGIC, $this->lastNode));
  5549. fclose($init);
  5550. if ($writen !== self::INT32_SIZE * 2) {
  5551. throw new Nette\InvalidStateException("Cannot write journal header.");
  5552. }
  5553. }
  5554. }
  5555. $this->handle = fopen($this->file, 'r+b');
  5556. if (!$this->handle) {
  5557. throw new Nette\InvalidStateException("Cannot open journal file '$this->file'.");
  5558. }
  5559. if (!flock($this->handle, LOCK_SH)) {
  5560. throw new Nette\InvalidStateException('Cannot acquire shared lock on journal.');
  5561. }
  5562. $header = stream_get_contents($this->handle, 2 * self::INT32_SIZE, 0);
  5563. flock($this->handle, LOCK_UN);
  5564. list(, $fileMagic, $this->lastNode) = unpack('N2', $header);
  5565. if ($fileMagic !== self::FILE_MAGIC) {
  5566. fclose($this->handle);
  5567. $this->handle = false;
  5568. throw new Nette\InvalidStateException("Malformed journal file '$this->file'.");
  5569. }
  5570. }
  5571. function __destruct()
  5572. {
  5573. if ($this->handle) {
  5574. $this->headerCommit();
  5575. flock($this->handle, LOCK_UN);
  5576. fclose($this->handle);
  5577. $this->handle = false;
  5578. }
  5579. }
  5580. function write($key, array $dependencies)
  5581. {
  5582. $this->lock();
  5583. $priority = !isset($dependencies[Cache::PRIORITY]) ? FALSE : (int) $dependencies[Cache::PRIORITY];
  5584. $tags = empty($dependencies[Cache::TAGS]) ? FALSE : (array) $dependencies[Cache::TAGS];
  5585. $exists = FALSE;
  5586. $keyHash = crc32($key);
  5587. list($entriesNodeId, $entriesNode) = $this->findIndexNode(self::ENTRIES, $keyHash);
  5588. if (isset($entriesNode[$keyHash])) {
  5589. $entries = $this->mergeIndexData($entriesNode[$keyHash]);
  5590. foreach ($entries as $link => $foo) {
  5591. $dataNode = $this->getNode($link >> self::BITROT);
  5592. if ($dataNode[$link][self::KEY] === $key) {
  5593. if ($dataNode[$link][self::TAGS] == $tags && $dataNode[$link][self::PRIORITY] === $priority) {
  5594. if ($dataNode[$link][self::DELETED]) {
  5595. $dataNode[$link][self::DELETED] = FALSE;
  5596. $this->saveNode($link >> self::BITROT, $dataNode);
  5597. }
  5598. $exists = TRUE;
  5599. } else {
  5600. $toDelete = array();
  5601. foreach ($dataNode[$link][self::TAGS] as $tag) {
  5602. $toDelete[self::TAGS][$tag][$link] = TRUE;
  5603. }
  5604. if ($dataNode[$link][self::PRIORITY] !== FALSE) {
  5605. $toDelete[self::PRIORITY][$dataNode[$link][self::PRIORITY]][$link] = TRUE;
  5606. }
  5607. $toDelete[self::ENTRIES][$keyHash][$link] = TRUE;
  5608. $this->cleanFromIndex($toDelete);
  5609. $entriesNode = $this->getNode($entriesNodeId);
  5610. unset($dataNode[$link]);
  5611. $this->saveNode($link >> self::BITROT, $dataNode);
  5612. }
  5613. break;
  5614. }
  5615. }
  5616. }
  5617. if ($exists === FALSE) {
  5618. $requiredSize = strlen($key) + 75;
  5619. if ($tags) {
  5620. foreach ($tags as $tag) {
  5621. $requiredSize += strlen($tag) + 13;
  5622. }
  5623. }
  5624. $requiredSize += $priority ? 10 : 1;
  5625. $freeDataNode = $this->findFreeDataNode($requiredSize);
  5626. $data = $this->getNode($freeDataNode);
  5627. if ($data === FALSE) {
  5628. $data = array(
  5629. self::INFO => array(
  5630. self::LAST_INDEX => ($freeDataNode << self::BITROT),
  5631. self::TYPE => self::DATA,
  5632. )
  5633. );
  5634. }
  5635. $dataNodeKey = ++$data[self::INFO][self::LAST_INDEX];
  5636. $data[$dataNodeKey] = array(
  5637. self::KEY => $key,
  5638. self::TAGS => $tags ? $tags : array(),
  5639. self::PRIORITY => $priority,
  5640. self::DELETED => FALSE,
  5641. );
  5642. $this->saveNode($freeDataNode, $data);
  5643. $entriesNode[$keyHash][$dataNodeKey] = 1;
  5644. $this->saveNode($entriesNodeId, $entriesNode);
  5645. if ($tags) {
  5646. foreach ($tags as $tag) {
  5647. list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag);
  5648. $node[$tag][$dataNodeKey] = 1;
  5649. $this->saveNode($nodeId, $node);
  5650. }
  5651. }
  5652. if ($priority) {
  5653. list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority);
  5654. $node[$priority][$dataNodeKey] = 1;
  5655. $this->saveNode($nodeId, $node);
  5656. }
  5657. }
  5658. $this->commit();
  5659. $this->unlock();
  5660. }
  5661. function clean(array $conditions)
  5662. {
  5663. $this->lock();
  5664. if (!empty($conditions[Cache::ALL])) {
  5665. $this->nodeCache = $this->nodeChanged = $this->dataNodeFreeSpace = array();
  5666. $this->deleteAll();
  5667. $this->unlock();
  5668. return;
  5669. }
  5670. $toDelete = array(
  5671. self::TAGS => array(),
  5672. self::PRIORITY => array(),
  5673. self::ENTRIES => array()
  5674. );
  5675. $entries = array();
  5676. if (!empty($conditions[Cache::TAGS])) {
  5677. $entries = $this->cleanTags((array) $conditions[Cache::TAGS], $toDelete);
  5678. }
  5679. if (isset($conditions[Cache::PRIORITY])) {
  5680. $this->arrayAppend($entries, $this->cleanPriority((int) $conditions[Cache::PRIORITY], $toDelete));
  5681. }
  5682. $this->deletedLinks = array();
  5683. $this->cleanFromIndex($toDelete);
  5684. $this->commit();
  5685. $this->unlock();
  5686. return $entries;
  5687. }
  5688. private function cleanTags(array $tags, array &$toDelete)
  5689. {
  5690. $entries = array();
  5691. foreach ($tags as $tag) {
  5692. list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag);
  5693. if (isset($node[$tag])) {
  5694. $ent = $this->cleanLinks($this->mergeIndexData($node[$tag]), $toDelete);
  5695. $this->arrayAppend($entries, $ent);
  5696. }
  5697. }
  5698. return $entries;
  5699. }
  5700. private function cleanPriority($priority, array &$toDelete)
  5701. {
  5702. list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority);
  5703. ksort($node);
  5704. $allData = array();
  5705. foreach ($node as $prior => $data) {
  5706. if ($prior === self::INFO) {
  5707. continue;
  5708. } elseif ($prior > $priority) {
  5709. break;
  5710. }
  5711. $this->arrayAppendKeys($allData, $this->mergeIndexData($data));
  5712. }
  5713. $nodeInfo = $node[self::INFO];
  5714. while ($nodeInfo[self::PREV_NODE] !== -1) {
  5715. $nodeId = $nodeInfo[self::PREV_NODE];
  5716. $node = $this->getNode($nodeId);
  5717. if ($node === FALSE) {
  5718. if (self::$debug) {
  5719. throw new Nette\InvalidStateException("Cannot load node number $nodeId.");
  5720. }
  5721. break;
  5722. }
  5723. $nodeInfo = $node[self::INFO];
  5724. unset($node[self::INFO]);
  5725. foreach ($node as $prior => $data) {
  5726. $this->arrayAppendKeys($allData, $this->mergeIndexData($data));
  5727. }
  5728. }
  5729. return $this->cleanLinks($allData, $toDelete);
  5730. }
  5731. private function cleanLinks(array $data, array &$toDelete)
  5732. {
  5733. $return = array();
  5734. $data = array_keys($data);
  5735. sort($data);
  5736. $max = count($data);
  5737. $data[] = 0;
  5738. $i = 0;
  5739. while ($i < $max) {
  5740. $searchLink = $data[$i];
  5741. if (isset($this->deletedLinks[$searchLink])) {
  5742. ++$i;
  5743. continue;
  5744. }
  5745. $nodeId = $searchLink >> self::BITROT;
  5746. $node = $this->getNode($nodeId);
  5747. if ($node === FALSE) {
  5748. if (self::$debug) {
  5749. throw new Nette\InvalidStateException('Cannot load node number ' . ($nodeId) . '.');
  5750. }
  5751. ++$i;
  5752. continue;
  5753. }
  5754. do {
  5755. $link = $data[$i];
  5756. if (!isset($node[$link])) {
  5757. if (self::$debug) {
  5758. throw new Nette\InvalidStateException("Link with ID $searchLink is not in node ". ($nodeId) . '.');
  5759. }
  5760. continue;
  5761. } elseif (isset($this->deletedLinks[$link])) {
  5762. continue;
  5763. }
  5764. $nodeLink = &$node[$link];
  5765. if (!$nodeLink[self::DELETED]) {
  5766. $nodeLink[self::DELETED] = TRUE;
  5767. $return[] = $nodeLink[self::KEY];
  5768. } else {
  5769. foreach ($nodeLink[self::TAGS] as $tag) {
  5770. $toDelete[self::TAGS][$tag][$link] = TRUE;
  5771. }
  5772. if ($nodeLink[self::PRIORITY] !== FALSE) {
  5773. $toDelete[self::PRIORITY][$nodeLink[self::PRIORITY]][$link] = TRUE;
  5774. }
  5775. $toDelete[self::ENTRIES][crc32($nodeLink[self::KEY])][$link] = TRUE;
  5776. unset($node[$link]);
  5777. $this->deletedLinks[$link] = TRUE;
  5778. }
  5779. } while (($data[++$i] >> self::BITROT) === $nodeId);
  5780. $this->saveNode($nodeId, $node);
  5781. }
  5782. return $return;
  5783. }
  5784. private function cleanFromIndex(array $toDeleteFromIndex)
  5785. {
  5786. foreach ($toDeleteFromIndex as $type => $toDelete) {
  5787. ksort($toDelete);
  5788. while (!empty($toDelete)) {
  5789. reset($toDelete);
  5790. $searchKey = key($toDelete);
  5791. list($masterNodeId, $masterNode) = $this->findIndexNode($type, $searchKey);
  5792. if (!isset($masterNode[$searchKey])) {
  5793. if (self::$debug) {
  5794. throw new Nette\InvalidStateException('Bad index.');
  5795. }
  5796. unset($toDelete[$searchKey]);
  5797. continue;
  5798. }
  5799. foreach ($toDelete as $key => $links) {
  5800. if (isset($masterNode[$key])) {
  5801. foreach ($links as $link => $foo) {
  5802. if (isset($masterNode[$key][$link])) {
  5803. unset($masterNode[$key][$link], $links[$link]);
  5804. }
  5805. }
  5806. if (!empty($links) && isset($masterNode[$key][self::INDEX_DATA])) {
  5807. $this->cleanIndexData($masterNode[$key][self::INDEX_DATA], $links, $masterNode[$key]);
  5808. }
  5809. if (empty($masterNode[$key])) {
  5810. unset($masterNode[$key]);
  5811. }
  5812. unset($toDelete[$key]);
  5813. } else {
  5814. break;
  5815. }
  5816. }
  5817. $this->saveNode($masterNodeId, $masterNode);
  5818. }
  5819. }
  5820. }
  5821. private function mergeIndexData(array $data)
  5822. {
  5823. while (isset($data[self::INDEX_DATA])) {
  5824. $id = $data[self::INDEX_DATA];
  5825. unset($data[self::INDEX_DATA]);
  5826. $childNode = $this->getNode($id);
  5827. if ($childNode === FALSE) {
  5828. if (self::$debug) {
  5829. throw new Nette\InvalidStateException("Cannot load node number $id.");
  5830. }
  5831. break;
  5832. }
  5833. $this->arrayAppendKeys($data, $childNode[self::INDEX_DATA]);
  5834. }
  5835. return $data;
  5836. }
  5837. private function cleanIndexData($nextNodeId, array $links, &$masterNodeLink)
  5838. {
  5839. $prev = -1;
  5840. while ($nextNodeId && !empty($links)) {
  5841. $nodeId = $nextNodeId;
  5842. $node = $this->getNode($nodeId);
  5843. if ($node === FALSE) {
  5844. if (self::$debug) {
  5845. throw new Nette\InvalidStateException("Cannot load node number $nodeId.");
  5846. }
  5847. break;
  5848. }
  5849. foreach ($links as $link => $foo) {
  5850. if (isset($node[self::INDEX_DATA][$link])) {
  5851. unset($node[self::INDEX_DATA][$link], $links[$link]);
  5852. }
  5853. }
  5854. if (isset($node[self::INDEX_DATA][self::INDEX_DATA])) {
  5855. $nextNodeId = $node[self::INDEX_DATA][self::INDEX_DATA];
  5856. } else {
  5857. $nextNodeId = FALSE;
  5858. }
  5859. if (empty($node[self::INDEX_DATA]) || (count($node[self::INDEX_DATA]) === 1 && $nextNodeId)) {
  5860. if ($prev === -1) {
  5861. if ($nextNodeId === FALSE) {
  5862. unset($masterNodeLink[self::INDEX_DATA]);
  5863. } else {
  5864. $masterNodeLink[self::INDEX_DATA] = $nextNodeId;
  5865. }
  5866. } else {
  5867. $prevNode = $this->getNode($prev);
  5868. if ($prevNode === FALSE) {
  5869. if (self::$debug) {
  5870. throw new Nette\InvalidStateException("Cannot load node number $prev.");
  5871. }
  5872. } else {
  5873. if ($nextNodeId === FALSE) {
  5874. unset($prevNode[self::INDEX_DATA][self::INDEX_DATA]);
  5875. if (empty($prevNode[self::INDEX_DATA])) {
  5876. unset($prevNode[self::INDEX_DATA]);
  5877. }
  5878. } else {
  5879. $prevNode[self::INDEX_DATA][self::INDEX_DATA] = $nextNodeId;
  5880. }
  5881. $this->saveNode($prev, $prevNode);
  5882. }
  5883. }
  5884. unset($node[self::INDEX_DATA]);
  5885. } else {
  5886. $prev = $nodeId;
  5887. }
  5888. $this->saveNode($nodeId, $node);
  5889. }
  5890. }
  5891. private function getNode($id)
  5892. {
  5893. if (isset($this->nodeCache[$id])) {
  5894. return $this->nodeCache[$id];
  5895. }
  5896. $binary = stream_get_contents($this->handle, self::NODE_SIZE, self::HEADER_SIZE + self::NODE_SIZE * $id);
  5897. if (empty($binary)) {
  5898. return FALSE;
  5899. }
  5900. list(, $magic, $lenght) = unpack('N2', $binary);
  5901. if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) {
  5902. if (!empty($magic)) {
  5903. if (self::$debug) {
  5904. throw new Nette\InvalidStateException("Node $id has malformed header.");
  5905. }
  5906. $this->deleteNode($id);
  5907. }
  5908. return FALSE;
  5909. }
  5910. $data = substr($binary, 2 * self::INT32_SIZE, $lenght - 2 * self::INT32_SIZE);
  5911. $node = @unserialize($data);
  5912. if ($node === FALSE) {
  5913. $this->deleteNode($id);
  5914. if (self::$debug) {
  5915. throw new Nette\InvalidStateException("Cannot deserialize node number $id.");
  5916. }
  5917. return FALSE;
  5918. }
  5919. return $this->nodeCache[$id] = $node;
  5920. }
  5921. private function saveNode($id, array $node)
  5922. {
  5923. if (count($node) === 1) {
  5924. $nodeInfo = $node[self::INFO];
  5925. if ($nodeInfo[self::TYPE] !== self::DATA) {
  5926. if ($nodeInfo[self::END] !== -1) {
  5927. $this->nodeCache[$id] = $node;
  5928. $this->nodeChanged[$id] = TRUE;
  5929. return;
  5930. }
  5931. if ($nodeInfo[self::MAX] === -1) {
  5932. $max = PHP_INT_MAX;
  5933. } else {
  5934. $max = $nodeInfo[self::MAX];
  5935. }
  5936. list(, , $parentId) = $this->findIndexNode($nodeInfo[self::TYPE], $max, $id);
  5937. if ($parentId !== -1 && $parentId !== $id) {
  5938. $parentNode = $this->getNode($parentId);
  5939. if ($parentNode === FALSE) {
  5940. if (self::$debug) {
  5941. throw new Nette\InvalidStateException("Cannot load node number $parentId.");
  5942. }
  5943. } else {
  5944. if ($parentNode[self::INFO][self::END] === $id) {
  5945. if (count($parentNode) === 1) {
  5946. $parentNode[self::INFO][self::END] = -1;
  5947. } else {
  5948. end($parentNode);
  5949. $lastKey = key($parentNode);
  5950. $parentNode[self::INFO][self::END] = $parentNode[$lastKey];
  5951. unset($parentNode[$lastKey]);
  5952. }
  5953. } else {
  5954. unset($parentNode[$nodeInfo[self::MAX]]);
  5955. }
  5956. $this->saveNode($parentId, $parentNode);
  5957. }
  5958. }
  5959. if ($nodeInfo[self::TYPE] === self::PRIORITY) {
  5960. if ($nodeInfo[self::MAX] === -1) {
  5961. if ($nodeInfo[self::PREV_NODE] !== -1) {
  5962. $prevNode = $this->getNode($nodeInfo[self::PREV_NODE]);
  5963. if ($prevNode === FALSE) {
  5964. if (self::$debug) {
  5965. throw new Nette\InvalidStateException('Cannot load node number ' . $nodeInfo[self::PREV_NODE] . '.');
  5966. }
  5967. } else {
  5968. $prevNode[self::INFO][self::MAX] = -1;
  5969. $this->saveNode($nodeInfo[self::PREV_NODE], $prevNode);
  5970. }
  5971. }
  5972. } else {
  5973. list($nextId, $nextNode) = $this->findIndexNode($nodeInfo[self::TYPE], $nodeInfo[self::MAX] + 1, NULL, $id);
  5974. if ($nextId !== -1 && $nextId !== $id) {
  5975. $nextNode[self::INFO][self::PREV_NODE] = $nodeInfo[self::PREV_NODE];
  5976. $this->saveNode($nextId, $nextNode);
  5977. }
  5978. }
  5979. }
  5980. }
  5981. $this->nodeCache[$id] = FALSE;
  5982. } else {
  5983. $this->nodeCache[$id] = $node;
  5984. }
  5985. $this->nodeChanged[$id] = TRUE;
  5986. }
  5987. private function commit()
  5988. {
  5989. do {
  5990. foreach ($this->nodeChanged as $id => $foo) {
  5991. if ($this->prepareNode($id, $this->nodeCache[$id])) {
  5992. unset($this->nodeChanged[$id]);
  5993. }
  5994. }
  5995. } while (!empty($this->nodeChanged));
  5996. foreach ($this->toCommit as $node => $str) {
  5997. $this->commitNode($node, $str);
  5998. }
  5999. $this->toCommit = array();
  6000. }
  6001. private function prepareNode($id, $node)
  6002. {
  6003. if ($node === FALSE) {
  6004. if ($id < $this->lastNode) {
  6005. $this->lastNode = $id;
  6006. }
  6007. unset($this->nodeCache[$id]);
  6008. unset($this->dataNodeFreeSpace[$id]);
  6009. $this->deleteNode($id);
  6010. return TRUE;
  6011. }
  6012. $data = serialize($node);
  6013. $dataSize = strlen($data) + 2 * self::INT32_SIZE;
  6014. $isData = $node[self::INFO][self::TYPE] === self::DATA;
  6015. if ($dataSize > self::NODE_SIZE) {
  6016. if ($isData) {
  6017. throw new Nette\InvalidStateException('Saving node is bigger than maximum node size.');
  6018. } else {
  6019. $this->bisectNode($id, $node);
  6020. return FALSE;
  6021. }
  6022. }
  6023. $this->toCommit[$id] = pack('N2', $isData ? self::DATA_MAGIC : self::INDEX_MAGIC, $dataSize) . $data;
  6024. if ($this->lastNode < $id) {
  6025. $this->lastNode = $id;
  6026. }
  6027. if ($isData) {
  6028. $this->dataNodeFreeSpace[$id] = self::NODE_SIZE - $dataSize;
  6029. }
  6030. return TRUE;
  6031. }
  6032. private function commitNode($id, $str)
  6033. {
  6034. fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id);
  6035. $writen = fwrite($this->handle, $str);
  6036. if ($writen === FALSE) {
  6037. throw new Nette\InvalidStateException("Cannot write node number $id to journal.");
  6038. }
  6039. }
  6040. private function findIndexNode($type, $search, $childId = NULL, $prevId = NULL)
  6041. {
  6042. $nodeId = self::$startNode[$type];
  6043. $parentId = -1;
  6044. while (TRUE) {
  6045. $node = $this->getNode($nodeId);
  6046. if ($node === FALSE) {
  6047. return array(
  6048. $nodeId,
  6049. array(
  6050. self::INFO => array(
  6051. self::TYPE => $type,
  6052. self::IS_LEAF => TRUE,
  6053. self::PREV_NODE => -1,
  6054. self::END => -1,
  6055. self::MAX => -1,
  6056. )
  6057. ),
  6058. $parentId,
  6059. );
  6060. }
  6061. if ($node[self::INFO][self::IS_LEAF] || $nodeId === $childId || $node[self::INFO][self::PREV_NODE] === $prevId) {
  6062. return array($nodeId, $node, $parentId);
  6063. }
  6064. $parentId = $nodeId;
  6065. if (isset($node[$search])) {
  6066. $nodeId = $node[$search];
  6067. } else {
  6068. foreach ($node as $key => $childNode) {
  6069. if ($key > $search and $key !== self::INFO) {
  6070. $nodeId = $childNode;
  6071. continue 2;
  6072. }
  6073. }
  6074. $nodeId = $node[self::INFO][self::END];
  6075. }
  6076. }
  6077. }
  6078. private function findFreeNode($count = 1)
  6079. {
  6080. $id = $this->lastNode;
  6081. $nodesId = array();
  6082. do {
  6083. if (isset($this->nodeCache[$id])) {
  6084. ++$id;
  6085. continue;
  6086. }
  6087. $offset = self::HEADER_SIZE + self::NODE_SIZE * $id;
  6088. $binary = stream_get_contents($this->handle, self::INT32_SIZE, $offset);
  6089. if (empty($binary)) {
  6090. $nodesId[] = $id;
  6091. } else {
  6092. list(, $magic) = unpack('N', $binary);
  6093. if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) {
  6094. $nodesId[] = $id;
  6095. }
  6096. }
  6097. ++$id;
  6098. } while (count($nodesId) !== $count);
  6099. if ($count === 1) {
  6100. return $nodesId[0];
  6101. } else {
  6102. return $nodesId;
  6103. }
  6104. }
  6105. private function findFreeDataNode($size)
  6106. {
  6107. foreach ($this->dataNodeFreeSpace as $id => $freeSpace) {
  6108. if ($freeSpace > $size) {
  6109. return $id;
  6110. }
  6111. }
  6112. $id = self::$startNode[self::DATA];
  6113. while (TRUE) {
  6114. if (isset($this->dataNodeFreeSpace[$id]) || isset($this->nodeCache[$id])) {
  6115. ++$id;
  6116. continue;
  6117. }
  6118. $offset = self::HEADER_SIZE + self::NODE_SIZE * $id;
  6119. $binary = stream_get_contents($this->handle, 2 * self::INT32_SIZE, $offset);
  6120. if (empty($binary)) {
  6121. $this->dataNodeFreeSpace[$id] = self::NODE_SIZE;
  6122. return $id;
  6123. }
  6124. list(, $magic, $nodeSize) = unpack('N2', $binary);
  6125. if (empty($magic)) {
  6126. $this->dataNodeFreeSpace[$id] = self::NODE_SIZE;
  6127. return $id;
  6128. } elseif ($magic === self::DATA_MAGIC) {
  6129. $freeSpace = self::NODE_SIZE - $nodeSize;
  6130. $this->dataNodeFreeSpace[$id] = $freeSpace;
  6131. if ($freeSpace > $size) {
  6132. return $id;
  6133. }
  6134. }
  6135. ++$id;
  6136. }
  6137. }
  6138. private function bisectNode($id, array $node)
  6139. {
  6140. $nodeInfo = $node[self::INFO];
  6141. unset($node[self::INFO]);
  6142. if (count($node) === 1) {
  6143. $key = key($node);
  6144. $dataId = $this->findFreeDataNode(self::NODE_SIZE);
  6145. $this->saveNode($dataId, array(
  6146. self::INDEX_DATA => $node[$key],
  6147. self::INFO => array(
  6148. self::TYPE => self::DATA,
  6149. self::LAST_INDEX => ($dataId << self::BITROT),
  6150. )));
  6151. unset($node[$key]);
  6152. $node[$key][self::INDEX_DATA] = $dataId;
  6153. $node[self::INFO] = $nodeInfo;
  6154. $this->saveNode($id, $node);
  6155. return;
  6156. }
  6157. ksort($node);
  6158. $halfCount = ceil(count($node) / 2);
  6159. list($first, $second) = array_chunk($node, $halfCount, TRUE);
  6160. end($first);
  6161. $halfKey = key($first);
  6162. if ($id <= 2) {
  6163. list($firstId, $secondId) = $this->findFreeNode(2);
  6164. $first[self::INFO] = array(
  6165. self::TYPE => $nodeInfo[self::TYPE],
  6166. self::IS_LEAF => $nodeInfo[self::IS_LEAF],
  6167. self::PREV_NODE => -1,
  6168. self::END => -1,
  6169. self::MAX => $halfKey,
  6170. );
  6171. $this->saveNode($firstId, $first);
  6172. $second[self::INFO] = array(
  6173. self::TYPE => $nodeInfo[self::TYPE],
  6174. self::IS_LEAF => $nodeInfo[self::IS_LEAF],
  6175. self::PREV_NODE => $firstId,
  6176. self::END => $nodeInfo[self::END],
  6177. self::MAX => -1,
  6178. );
  6179. $this->saveNode($secondId, $second);
  6180. $parentNode = array(
  6181. self::INFO => array(
  6182. self::TYPE => $nodeInfo[self::TYPE],
  6183. self::IS_LEAF => FALSE,
  6184. self::PREV_NODE => -1,
  6185. self::END => $secondId,
  6186. self::MAX => -1,
  6187. ),
  6188. $halfKey => $firstId,
  6189. );
  6190. $this->saveNode($id, $parentNode);
  6191. } else {
  6192. $firstId = $this->findFreeNode();
  6193. $first[self::INFO] = array(
  6194. self::TYPE => $nodeInfo[self::TYPE],
  6195. self::IS_LEAF => $nodeInfo[self::IS_LEAF],
  6196. self::PREV_NODE => $nodeInfo[self::PREV_NODE],
  6197. self::END => -1,
  6198. self::MAX => $halfKey,
  6199. );
  6200. $this->saveNode($firstId, $first);
  6201. $second[self::INFO] = array(
  6202. self::TYPE => $nodeInfo[self::TYPE],
  6203. self::IS_LEAF => $nodeInfo[self::IS_LEAF],
  6204. self::PREV_NODE => $firstId,
  6205. self::END => $nodeInfo[self::END],
  6206. self::MAX => $nodeInfo[self::MAX],
  6207. );
  6208. $this->saveNode($id, $second);
  6209. list(,, $parent) = $this->findIndexNode($nodeInfo[self::TYPE], $halfKey);
  6210. $parentNode = $this->getNode($parent);
  6211. if ($parentNode === FALSE) {
  6212. if (self::$debug) {
  6213. throw new Nette\InvalidStateException("Cannot load node number $parent.");
  6214. }
  6215. } else {
  6216. $parentNode[$halfKey] = $firstId;
  6217. ksort($parentNode);
  6218. $this->saveNode($parent, $parentNode);
  6219. }
  6220. }
  6221. }
  6222. private function headerCommit()
  6223. {
  6224. fseek($this->handle, self::INT32_SIZE);
  6225. @fwrite($this->handle, pack('N', $this->lastNode));
  6226. }
  6227. private function deleteNode($id)
  6228. {
  6229. fseek($this->handle, 0, SEEK_END);
  6230. $end = ftell($this->handle);
  6231. if ($end <= (self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) {
  6232. $packedNull = pack('N', 0);
  6233. do {
  6234. $binary = stream_get_contents($this->handle, self::INT32_SIZE, (self::HEADER_SIZE + self::NODE_SIZE * --$id));
  6235. } while (empty($binary) || $binary === $packedNull);
  6236. if (!ftruncate($this->handle, self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) {
  6237. throw new Nette\InvalidStateException('Cannot truncate journal file.');
  6238. }
  6239. } else {
  6240. fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id);
  6241. $writen = fwrite($this->handle, pack('N', 0));
  6242. if ($writen !== self::INT32_SIZE) {
  6243. throw new Nette\InvalidStateException("Cannot delete node number $id from journal.");
  6244. }
  6245. }
  6246. }
  6247. private function deleteAll()
  6248. {
  6249. if (!ftruncate($this->handle, self::HEADER_SIZE)) {
  6250. throw new Nette\InvalidStateException('Cannot truncate journal file.');
  6251. }
  6252. }
  6253. private function lock()
  6254. {
  6255. if (!$this->handle) {
  6256. throw new Nette\InvalidStateException('File journal file is not opened');
  6257. }
  6258. if (!flock($this->handle, LOCK_EX)) {
  6259. throw new Nette\InvalidStateException('Cannot acquire exclusive lock on journal.');
  6260. }
  6261. if ($this->lastModTime !== NULL) {
  6262. clearstatcache();
  6263. if ($this->lastModTime < @filemtime($this->file)) {
  6264. $this->nodeCache = $this->dataNodeFreeSpace = array();
  6265. }
  6266. }
  6267. }
  6268. private function unlock()
  6269. {
  6270. if ($this->handle) {
  6271. fflush($this->handle);
  6272. flock($this->handle, LOCK_UN);
  6273. clearstatcache();
  6274. $this->lastModTime = @filemtime($this->file);
  6275. }
  6276. }
  6277. private function arrayAppend(array &$array, array $append)
  6278. {
  6279. foreach ($append as $value) {
  6280. $array[] = $value;
  6281. }
  6282. }
  6283. private function arrayAppendKeys(array &$array, array $append)
  6284. {
  6285. foreach ($append as $key => $value) {
  6286. $array[$key] = $value;
  6287. }
  6288. }
  6289. }
  6290. class FileStorage extends Nette\Object implements Nette\Caching\IStorage
  6291. {
  6292. const META_HEADER_LEN = 28,
  6293. META_TIME = 'time',
  6294. META_SERIALIZED = 'serialized',
  6295. META_EXPIRE = 'expire',
  6296. META_DELTA = 'delta',
  6297. META_ITEMS = 'di',
  6298. META_CALLBACKS = 'callbacks';
  6299. const FILE = 'file',
  6300. HANDLE = 'handle';
  6301. public static $gcProbability = 0.001;
  6302. public static $useDirectories = TRUE;
  6303. private $dir;
  6304. private $useDirs;
  6305. private $journal;
  6306. function __construct($dir, IJournal $journal = NULL)
  6307. {
  6308. $this->dir = realpath($dir);
  6309. if ($this->dir === FALSE) {
  6310. throw new Nette\DirectoryNotFoundException("Directory '$dir' not found.");
  6311. }
  6312. $this->useDirs = (bool) self::$useDirectories;
  6313. $this->journal = $journal;
  6314. if (mt_rand() / mt_getrandmax() < self::$gcProbability) {
  6315. $this->clean(array());
  6316. }
  6317. }
  6318. function read($key)
  6319. {
  6320. $meta = $this->readMetaAndLock($this->getCacheFile($key), LOCK_SH);
  6321. if ($meta && $this->verify($meta)) {
  6322. return $this->readData($meta);
  6323. } else {
  6324. return NULL;
  6325. }
  6326. }
  6327. private function verify($meta)
  6328. {
  6329. do {
  6330. if (!empty($meta[self::META_DELTA])) {
  6331. if (filemtime($meta[self::FILE]) + $meta[self::META_DELTA] < time()) {
  6332. break;
  6333. }
  6334. touch($meta[self::FILE]);
  6335. } elseif (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < time()) {
  6336. break;
  6337. }
  6338. if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
  6339. break;
  6340. }
  6341. if (!empty($meta[self::META_ITEMS])) {
  6342. foreach ($meta[self::META_ITEMS] as $depFile => $time) {
  6343. $m = $this->readMetaAndLock($depFile, LOCK_SH);
  6344. if ($m[self::META_TIME] !== $time || ($m && !$this->verify($m))) {
  6345. break 2;
  6346. }
  6347. }
  6348. }
  6349. return TRUE;
  6350. } while (FALSE);
  6351. $this->delete($meta[self::FILE], $meta[self::HANDLE]);
  6352. return FALSE;
  6353. }
  6354. function write($key, $data, array $dp)
  6355. {
  6356. $meta = array(
  6357. self::META_TIME => microtime(),
  6358. );
  6359. if (isset($dp[Cache::EXPIRATION])) {
  6360. if (empty($dp[Cache::SLIDING])) {
  6361. $meta[self::META_EXPIRE] = $dp[Cache::EXPIRATION] + time();
  6362. } else {
  6363. $meta[self::META_DELTA] = (int) $dp[Cache::EXPIRATION];
  6364. }
  6365. }
  6366. if (isset($dp[Cache::ITEMS])) {
  6367. foreach ((array) $dp[Cache::ITEMS] as $item) {
  6368. $depFile = $this->getCacheFile($item);
  6369. $m = $this->readMetaAndLock($depFile, LOCK_SH);
  6370. $meta[self::META_ITEMS][$depFile] = $m[self::META_TIME];
  6371. unset($m);
  6372. }
  6373. }
  6374. if (isset($dp[Cache::CALLBACKS])) {
  6375. $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
  6376. }
  6377. $cacheFile = $this->getCacheFile($key);
  6378. if ($this->useDirs && !is_dir($dir = dirname($cacheFile))) {
  6379. umask(0000);
  6380. if (!mkdir($dir, 0777)) {
  6381. return;
  6382. }
  6383. }
  6384. $handle = @fopen($cacheFile, 'r+b');
  6385. if (!$handle) {
  6386. $handle = fopen($cacheFile, 'wb');
  6387. if (!$handle) {
  6388. return;
  6389. }
  6390. }
  6391. if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) {
  6392. if (!$this->journal) {
  6393. throw new Nette\InvalidStateException('CacheJournal has not been provided.');
  6394. }
  6395. $this->journal->write($cacheFile, $dp);
  6396. }
  6397. flock($handle, LOCK_EX);
  6398. ftruncate($handle, 0);
  6399. if (!is_string($data)) {
  6400. $data = serialize($data);
  6401. $meta[self::META_SERIALIZED] = TRUE;
  6402. }
  6403. $head = serialize($meta) . '?>';
  6404. $head = '<?php //netteCache[01]' . str_pad((string) strlen($head), 6, '0', STR_PAD_LEFT) . $head;
  6405. $headLen = strlen($head);
  6406. $dataLen = strlen($data);
  6407. do {
  6408. if (fwrite($handle, str_repeat("\x00", $headLen), $headLen) !== $headLen) {
  6409. break;
  6410. }
  6411. if (fwrite($handle, $data, $dataLen) !== $dataLen) {
  6412. break;
  6413. }
  6414. fseek($handle, 0);
  6415. if (fwrite($handle, $head, $headLen) !== $headLen) {
  6416. break;
  6417. }
  6418. flock($handle, LOCK_UN);
  6419. fclose($handle);
  6420. return TRUE;
  6421. } while (FALSE);
  6422. $this->delete($cacheFile, $handle);
  6423. }
  6424. function remove($key)
  6425. {
  6426. $this->delete($this->getCacheFile($key));
  6427. }
  6428. function clean(array $conds)
  6429. {
  6430. $all = !empty($conds[Cache::ALL]);
  6431. $collector = empty($conds);
  6432. if ($all || $collector) {
  6433. $now = time();
  6434. foreach (Nette\Utils\Finder::find('*')->from($this->dir)->childFirst() as $entry) {
  6435. $path = (string) $entry;
  6436. if ($entry->isDir()) {
  6437. @rmdir($path);
  6438. continue;
  6439. }
  6440. if ($all) {
  6441. $this->delete($path);
  6442. } else {
  6443. $meta = $this->readMetaAndLock($path, LOCK_SH);
  6444. if (!$meta) {
  6445. continue;
  6446. }
  6447. if (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < $now) {
  6448. $this->delete($path, $meta[self::HANDLE]);
  6449. continue;
  6450. }
  6451. flock($meta[self::HANDLE], LOCK_UN);
  6452. fclose($meta[self::HANDLE]);
  6453. }
  6454. }
  6455. if ($this->journal) {
  6456. $this->journal->clean($conds);
  6457. }
  6458. return;
  6459. }
  6460. if ($this->journal) {
  6461. foreach ($this->journal->clean($conds) as $file) {
  6462. $this->delete($file);
  6463. }
  6464. }
  6465. }
  6466. protected function readMetaAndLock($file, $lock)
  6467. {
  6468. $handle = @fopen($file, 'r+b');
  6469. if (!$handle) {
  6470. return NULL;
  6471. }
  6472. flock($handle, $lock);
  6473. $head = stream_get_contents($handle, self::META_HEADER_LEN);
  6474. if ($head && strlen($head) === self::META_HEADER_LEN) {
  6475. $size = (int) substr($head, -6);
  6476. $meta = stream_get_contents($handle, $size, self::META_HEADER_LEN);
  6477. $meta = @unserialize($meta);
  6478. if (is_array($meta)) {
  6479. fseek($handle, $size + self::META_HEADER_LEN);
  6480. $meta[self::FILE] = $file;
  6481. $meta[self::HANDLE] = $handle;
  6482. return $meta;
  6483. }
  6484. }
  6485. flock($handle, LOCK_UN);
  6486. fclose($handle);
  6487. return NULL;
  6488. }
  6489. protected function readData($meta)
  6490. {
  6491. $data = stream_get_contents($meta[self::HANDLE]);
  6492. flock($meta[self::HANDLE], LOCK_UN);
  6493. fclose($meta[self::HANDLE]);
  6494. if (empty($meta[self::META_SERIALIZED])) {
  6495. return $data;
  6496. } else {
  6497. return @unserialize($data);
  6498. }
  6499. }
  6500. protected function getCacheFile($key)
  6501. {
  6502. $file = urlencode($key);
  6503. if ($this->useDirs && $a = strrpos($file, '%00')) {
  6504. $file = substr_replace($file, '/_', $a, 3);
  6505. }
  6506. return $this->dir . '/_' . $file;
  6507. }
  6508. private static function delete($file, $handle = NULL)
  6509. {
  6510. if (@unlink($file)) {
  6511. if ($handle) {
  6512. flock($handle, LOCK_UN);
  6513. fclose($handle);
  6514. }
  6515. return;
  6516. }
  6517. if (!$handle) {
  6518. $handle = @fopen($file, 'r+');
  6519. }
  6520. if ($handle) {
  6521. flock($handle, LOCK_EX);
  6522. ftruncate($handle, 0);
  6523. flock($handle, LOCK_UN);
  6524. fclose($handle);
  6525. @unlink($file);
  6526. }
  6527. }
  6528. }
  6529. class MemcachedStorage extends Nette\Object implements Nette\Caching\IStorage
  6530. {
  6531. const META_CALLBACKS = 'callbacks',
  6532. META_DATA = 'data',
  6533. META_DELTA = 'delta';
  6534. private $memcache;
  6535. private $prefix;
  6536. private $journal;
  6537. static function isAvailable()
  6538. {
  6539. return extension_loaded('memcache');
  6540. }
  6541. function __construct($host = 'localhost', $port = 11211, $prefix = '', IJournal $journal = NULL)
  6542. {
  6543. if (!self::isAvailable()) {
  6544. throw new Nette\NotSupportedException("PHP extension 'memcache' is not loaded.");
  6545. }
  6546. $this->prefix = $prefix;
  6547. $this->journal = $journal;
  6548. $this->memcache = new \Memcache;
  6549. Nette\Diagnostics\Debugger::tryError();
  6550. $this->memcache->connect($host, $port);
  6551. if (Nette\Diagnostics\Debugger::catchError($e)) {
  6552. throw new Nette\InvalidStateException('Memcache::connect(): ' . $e->getMessage(), 0, $e);
  6553. }
  6554. }
  6555. function read($key)
  6556. {
  6557. $key = $this->prefix . $key;
  6558. $meta = $this->memcache->get($key);
  6559. if (!$meta) {
  6560. return NULL;
  6561. }
  6562. if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
  6563. $this->memcache->delete($key, 0);
  6564. return NULL;
  6565. }
  6566. if (!empty($meta[self::META_DELTA])) {
  6567. $this->memcache->replace($key, $meta, 0, $meta[self::META_DELTA] + time());
  6568. }
  6569. return $meta[self::META_DATA];
  6570. }
  6571. function write($key, $data, array $dp)
  6572. {
  6573. if (isset($dp[Cache::ITEMS])) {
  6574. throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
  6575. }
  6576. $key = $this->prefix . $key;
  6577. $meta = array(
  6578. self::META_DATA => $data,
  6579. );
  6580. $expire = 0;
  6581. if (isset($dp[Cache::EXPIRATION])) {
  6582. $expire = (int) $dp[Cache::EXPIRATION];
  6583. if (!empty($dp[Cache::SLIDING])) {
  6584. $meta[self::META_DELTA] = $expire;
  6585. }
  6586. }
  6587. if (isset($dp[Cache::CALLBACKS])) {
  6588. $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
  6589. }
  6590. if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) {
  6591. if (!$this->journal) {
  6592. throw new Nette\InvalidStateException('CacheJournal has not been provided.');
  6593. }
  6594. $this->journal->write($key, $dp);
  6595. }
  6596. $this->memcache->set($key, $meta, 0, $expire);
  6597. }
  6598. function remove($key)
  6599. {
  6600. $this->memcache->delete($this->prefix . $key, 0);
  6601. }
  6602. function clean(array $conds)
  6603. {
  6604. if (!empty($conds[Cache::ALL])) {
  6605. $this->memcache->flush();
  6606. } elseif ($this->journal) {
  6607. foreach ($this->journal->clean($conds) as $entry) {
  6608. $this->memcache->delete($entry, 0);
  6609. }
  6610. }
  6611. }
  6612. }
  6613. class MemoryStorage extends Nette\Object implements Nette\Caching\IStorage
  6614. {
  6615. private $data = array();
  6616. function read($key)
  6617. {
  6618. return isset($this->data[$key]) ? $this->data[$key] : NULL;
  6619. }
  6620. function write($key, $data, array $dp)
  6621. {
  6622. $this->data[$key] = $data;
  6623. }
  6624. function remove($key)
  6625. {
  6626. unset($this->data[$key]);
  6627. }
  6628. function clean(array $conds)
  6629. {
  6630. if (!empty($conds[Nette\Caching\Cache::ALL])) {
  6631. $this->data = array();
  6632. }
  6633. }
  6634. }
  6635. class PhpFileStorage extends FileStorage
  6636. {
  6637. public $hint;
  6638. protected function readData($meta)
  6639. {
  6640. return array(
  6641. 'file' => $meta[self::FILE],
  6642. 'handle' => $meta[self::HANDLE],
  6643. );
  6644. }
  6645. protected function getCacheFile($key)
  6646. {
  6647. return parent::getCacheFile(substr_replace(
  6648. $key,
  6649. trim(strtr($this->hint, '\\/@', '.._'), '.') . '-',
  6650. strpos($key, Nette\Caching\Cache::NAMESPACE_SEPARATOR) + 1,
  6651. 0
  6652. )) . '.php';
  6653. }
  6654. }
  6655. }
  6656. namespace Nette {
  6657. use Nette;
  6658. class ArrayHash implements \ArrayAccess, \Countable, \IteratorAggregate
  6659. {
  6660. static function from($arr, $recursive = TRUE)
  6661. {
  6662. $obj = new static;
  6663. foreach ($arr as $key => $value) {
  6664. if ($recursive && is_array($value)) {
  6665. $obj->$key = static::from($value, TRUE);
  6666. } else {
  6667. $obj->$key = $value;
  6668. }
  6669. }
  6670. return $obj;
  6671. }
  6672. function getIterator()
  6673. {
  6674. return new \RecursiveArrayIterator($this);
  6675. }
  6676. function count()
  6677. {
  6678. return count((array) $this);
  6679. }
  6680. function offsetSet($key, $value)
  6681. {
  6682. if (!is_scalar($key)) {
  6683. throw new InvalidArgumentException("Key must be either a string or an integer, " . gettype($key) ." given.");
  6684. }
  6685. $this->$key = $value;
  6686. }
  6687. function offsetGet($key)
  6688. {
  6689. return $this->$key;
  6690. }
  6691. function offsetExists($key)
  6692. {
  6693. return isset($this->$key);
  6694. }
  6695. function offsetUnset($key)
  6696. {
  6697. unset($this->$key);
  6698. }
  6699. }
  6700. final class Callback extends Object
  6701. {
  6702. private $cb;
  6703. function __construct($t, $m = NULL)
  6704. {
  6705. if ($m === NULL) {
  6706. if (is_string($t)) {
  6707. $t = explode('::', $t, 2);
  6708. $this->cb = isset($t[1]) ? $t : $t[0];
  6709. } elseif (is_object($t)) {
  6710. $this->cb = $t instanceof \Closure ? $t : array($t, '__invoke');
  6711. } else {
  6712. $this->cb = $t;
  6713. }
  6714. } else {
  6715. $this->cb = array($t, $m);
  6716. }
  6717. if (!is_callable($this->cb, TRUE)) {
  6718. throw new InvalidArgumentException("Invalid callback.");
  6719. }
  6720. }
  6721. function __invoke()
  6722. {
  6723. if (!is_callable($this->cb)) {
  6724. throw new InvalidStateException("Callback '$this' is not callable.");
  6725. }
  6726. $args = func_get_args();
  6727. return call_user_func_array($this->cb, $args);
  6728. }
  6729. function invoke()
  6730. {
  6731. if (!is_callable($this->cb)) {
  6732. throw new InvalidStateException("Callback '$this' is not callable.");
  6733. }
  6734. $args = func_get_args();
  6735. return call_user_func_array($this->cb, $args);
  6736. }
  6737. function invokeArgs(array $args)
  6738. {
  6739. if (!is_callable($this->cb)) {
  6740. throw new InvalidStateException("Callback '$this' is not callable.");
  6741. }
  6742. return call_user_func_array($this->cb, $args);
  6743. }
  6744. function invokeNamedArgs(array $args)
  6745. {
  6746. $ref = $this->toReflection();
  6747. if (is_array($this->cb)) {
  6748. return $ref->invokeNamedArgs(is_object($this->cb[0]) ? $this->cb[0] : NULL, $args);
  6749. } else {
  6750. return $ref->invokeNamedArgs($args);
  6751. }
  6752. }
  6753. function isCallable()
  6754. {
  6755. return is_callable($this->cb);
  6756. }
  6757. function getNative()
  6758. {
  6759. return $this->cb;
  6760. }
  6761. function toReflection()
  6762. {
  6763. if (is_array($this->cb)) {
  6764. return new Nette\Reflection\Method($this->cb[0], $this->cb[1]);
  6765. } else {
  6766. return new Nette\Reflection\GlobalFunction($this->cb);
  6767. }
  6768. }
  6769. function isStatic()
  6770. {
  6771. return is_array($this->cb) ? is_string($this->cb[0]) : is_string($this->cb);
  6772. }
  6773. function __toString()
  6774. {
  6775. if ($this->cb instanceof \Closure) {
  6776. return '{closure}';
  6777. } elseif (is_string($this->cb) && $this->cb[0] === "\0") {
  6778. return '{lambda}';
  6779. } else {
  6780. is_callable($this->cb, TRUE, $textual);
  6781. return $textual;
  6782. }
  6783. }
  6784. }
  6785. use Nette\Caching\Cache;use Nette\DI;
  6786. class Configurator extends Object
  6787. {
  6788. public static $instance;
  6789. public $defaultConfigFile = '%appDir%/config.neon';
  6790. private $container;
  6791. function __construct($containerClass = 'Nette\DI\Container')
  6792. {
  6793. self::$instance = $this;
  6794. $this->container = new $containerClass;
  6795. foreach (get_class_methods($this) as $name) {
  6796. if (substr($name, 0, 13) === 'createService' ) {
  6797. $this->container->addService(strtolower($name[13]) . substr($name, 14), array(get_called_class(), $name));
  6798. }
  6799. }
  6800. $this->container->params = new ArrayHash;
  6801. defined('WWW_DIR') && $this->container->params['wwwDir'] = realpath(WWW_DIR);
  6802. defined('APP_DIR') && $this->container->params['appDir'] = realpath(APP_DIR);
  6803. defined('LIBS_DIR') && $this->container->params['libsDir'] = realpath(LIBS_DIR);
  6804. defined('TEMP_DIR') && $this->container->params['tempDir'] = realpath(TEMP_DIR);
  6805. $this->container->params['productionMode'] = self::detectProductionMode();
  6806. $this->container->params['consoleMode'] = PHP_SAPI === 'cli';
  6807. }
  6808. function getContainer()
  6809. {
  6810. return $this->container;
  6811. }
  6812. function loadConfig($file, $section = NULL)
  6813. {
  6814. if ($file === NULL) {
  6815. $file = $this->defaultConfigFile;
  6816. }
  6817. $container = $this->container;
  6818. $file = $container->expand($file);
  6819. if (!is_file($file)) {
  6820. $file = preg_replace('#\.neon$#', '.ini', $file);
  6821. }
  6822. if ($section === NULL) {
  6823. if (PHP_SAPI === 'cli') {
  6824. $section = Environment::CONSOLE;
  6825. } else {
  6826. $section = $container->params['productionMode'] ? Environment::PRODUCTION : Environment::DEVELOPMENT;
  6827. }
  6828. }
  6829. $cache = new Cache($container->templateCacheStorage, 'Nette.Configurator');
  6830. $cacheKey = array((array) $container->params, $file, $section);
  6831. $cached = $cache->load($cacheKey);
  6832. if ($cached) {
  6833. require $cached['file'];
  6834. fclose($cached['handle']);
  6835. return;
  6836. }
  6837. $config = Nette\Config\Config::fromFile($file, $section);
  6838. $code = "<?php\n// source file $file\n\n";
  6839. foreach (array('service', 'variable') as $item) {
  6840. if (isset($config[$item])) {
  6841. trigger_error(basename($file) . ": Section '$item' is deprecated; use plural form '{$item}s' instead.", E_USER_WARNING);
  6842. $config[$item . 's'] = $config[$item];
  6843. unset($config[$item]);
  6844. }
  6845. }
  6846. while (!empty($config['variables'])) {
  6847. $old = $config['variables'];
  6848. foreach ($config['variables'] as $key => $value) {
  6849. try {
  6850. $code .= $this->generateCode('$container->params[?] = ?', $key, $container->params[$key] = $container->expand($value));
  6851. unset($config['variables'][$key]);
  6852. } catch (Nette\InvalidArgumentException $e) {}
  6853. }
  6854. if ($old === $config['variables']) {
  6855. throw new InvalidStateException("Unable to expand variables: " . implode(', ', array_keys($old)) . ".");
  6856. }
  6857. }
  6858. unset($config['variables']);
  6859. if (isset($config['services'])) {
  6860. foreach ($config['services'] as $key => & $def) {
  6861. if (preg_match('#^Nette\\\\.*\\\\I?([a-zA-Z]+)$#', strtr($key, '-', '\\'), $m)) {
  6862. $m[1][0] = strtolower($m[1][0]);
  6863. trigger_error(basename($file) . ": service name '$key' has been renamed to '$m[1]'", E_USER_WARNING);
  6864. $key = $m[1];
  6865. }
  6866. if (is_scalar($def)) {
  6867. $def = array('class' => $def);
  6868. }
  6869. if (method_exists(get_called_class(), "createService$key")) {
  6870. $container->removeService($key);
  6871. if (!isset($def['factory']) && !isset($def['class'])) {
  6872. $def['factory'] = array(get_called_class(), "createService$key");
  6873. }
  6874. }
  6875. if (isset($def['option'])) {
  6876. $def['arguments'][] = $def['option'];
  6877. }
  6878. if (!empty($def['run'])) {
  6879. $def['tags'] = array('run');
  6880. }
  6881. }
  6882. $builder = new DI\ContainerBuilder;
  6883. $code .= $builder->generateCode($config['services']);
  6884. unset($config['services']);
  6885. }
  6886. array_walk_recursive($config, function(&$val) {
  6887. $val = Environment::expand($val);
  6888. });
  6889. if (isset($config['php'])) {
  6890. foreach ($config['php'] as $key => $value) {
  6891. if (is_array($value)) {
  6892. foreach ($value as $k => $v) {
  6893. $code .= $this->configurePhp("$key.$k", $v);
  6894. }
  6895. } else {
  6896. $code .= $this->configurePhp($key, $value);
  6897. }
  6898. }
  6899. unset($config['php']);
  6900. }
  6901. if (isset($config['const'])) {
  6902. foreach ($config['const'] as $key => $value) {
  6903. $code .= $this->generateCode('define', $key, $value);
  6904. }
  6905. unset($config['const']);
  6906. }
  6907. if (isset($config['mode'])) {
  6908. trigger_error(basename($file) . ": Section 'mode' is deprecated; use 'params' instead.", E_USER_WARNING);
  6909. foreach ($config['mode'] as $mode => $state) {
  6910. $code .= $this->generateCode('$container->params[?] = ?', $mode . 'Mode', (bool) $state);
  6911. }
  6912. unset($config['mode']);
  6913. }
  6914. foreach ($config as $key => $value) {
  6915. $code .= $this->generateCode('$container->params[?] = ' . (is_array($value) ? 'Nette\ArrayHash::from(?)' : '?'), $key, $value);
  6916. }
  6917. $code .= self::preloadEnvironment($container);
  6918. $code .= 'foreach ($container->getServiceNamesByTag("run") as $name => $foo) { $container->getService($name); }' . "\n";
  6919. $cache->save($cacheKey, $code, array(
  6920. Cache::FILES => $file,
  6921. ));
  6922. Nette\Utils\LimitedScope::evaluate($code, array('container' => $container));
  6923. }
  6924. static function detectProductionMode()
  6925. {
  6926. if (!isset($_SERVER['SERVER_ADDR']) && !isset($_SERVER['LOCAL_ADDR'])) {
  6927. return TRUE;
  6928. }
  6929. $addrs = array();
  6930. if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  6931. $addrs = preg_split('#,\s*#', $_SERVER['HTTP_X_FORWARDED_FOR']);
  6932. }
  6933. if (isset($_SERVER['REMOTE_ADDR'])) {
  6934. $addrs[] = $_SERVER['REMOTE_ADDR'];
  6935. }
  6936. $addrs[] = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
  6937. foreach ($addrs as $addr) {
  6938. $oct = explode('.', $addr);
  6939. if ($addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
  6940. && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')))
  6941. ) {
  6942. return TRUE;
  6943. }
  6944. }
  6945. return FALSE;
  6946. }
  6947. function configurePhp($name, $value)
  6948. {
  6949. if (!is_scalar($value)) {
  6950. throw new Nette\InvalidStateException("Configuration value for directive '$name' is not scalar.");
  6951. }
  6952. switch ($name) {
  6953. case 'include_path':
  6954. return $this->generateCode('set_include_path', str_replace(';', PATH_SEPARATOR, $value));
  6955. case 'ignore_user_abort':
  6956. return $this->generateCode('ignore_user_abort', $value);
  6957. case 'max_execution_time':
  6958. return $this->generateCode('set_time_limit', $value);
  6959. case 'date.timezone':
  6960. return $this->generateCode('date_default_timezone_set', $value);
  6961. }
  6962. if (function_exists('ini_set')) {
  6963. return $this->generateCode('ini_set', $name, $value);
  6964. } elseif (ini_get($name) != $value && !Framework::$iAmUsingBadHost) {
  6965. throw new Nette\NotSupportedException('Required function ini_set() is disabled.');
  6966. }
  6967. }
  6968. private static function generateCode($statement)
  6969. {
  6970. $args = func_get_args();
  6971. unset($args[0]);
  6972. foreach ($args as &$arg) {
  6973. $arg = var_export($arg, TRUE);
  6974. }
  6975. if (strpos($statement, '?') === FALSE) {
  6976. return $statement .= '(' . implode(', ', $args) . ");\n\n";
  6977. }
  6978. $a = strpos($statement, '?');
  6979. $i = 1;
  6980. while ($a !== FALSE) {
  6981. $statement = substr_replace($statement, $args[$i], $a, 1);
  6982. $a = strpos($statement, '?', $a + strlen($args[$i]));
  6983. $i++;
  6984. }
  6985. return $statement . ";\n\n";
  6986. }
  6987. static function createServiceApplication(DI\Container $container, array $options = NULL)
  6988. {
  6989. $context = new DI\Container;
  6990. $context->addService('httpRequest', $container->httpRequest);
  6991. $context->addService('httpResponse', $container->httpResponse);
  6992. $context->addService('session', $container->session);
  6993. $context->addService('presenterFactory', $container->presenterFactory);
  6994. $context->addService('router', $container->router);
  6995. Nette\Application\UI\Presenter::$invalidLinkMode = $container->params['productionMode']
  6996. ? Nette\Application\UI\Presenter::INVALID_LINK_SILENT
  6997. : Nette\Application\UI\Presenter::INVALID_LINK_WARNING;
  6998. $class = isset($options['class']) ? $options['class'] : 'Nette\Application\Application';
  6999. $application = new $class($context);
  7000. $application->catchExceptions = $container->params['productionMode'];
  7001. if ($container->session->exists()) {
  7002. $application->onStartup[] = function() use($container) {
  7003. $container->session->start();
  7004. };
  7005. }
  7006. return $application;
  7007. }
  7008. static function createServicePresenterFactory(DI\Container $container)
  7009. {
  7010. return new Nette\Application\PresenterFactory(
  7011. isset($container->params['appDir']) ? $container->params['appDir'] : NULL,
  7012. $container
  7013. );
  7014. }
  7015. static function createServiceRouter(DI\Container $container)
  7016. {
  7017. return new Nette\Application\Routers\RouteList;
  7018. }
  7019. static function createServiceHttpRequest()
  7020. {
  7021. $factory = new Nette\Http\RequestFactory;
  7022. $factory->setEncoding('UTF-8');
  7023. return $factory->createHttpRequest();
  7024. }
  7025. static function createServiceHttpResponse()
  7026. {
  7027. $response = new Nette\Http\Response;
  7028. if (!$response->isSent()) {
  7029. $response->setContentType('text/html', 'utf-8');
  7030. }
  7031. return $response;
  7032. }
  7033. static function createServiceHttpContext(DI\Container $container)
  7034. {
  7035. return new Nette\Http\Context($container->httpRequest, $container->httpResponse);
  7036. }
  7037. static function createServiceSession(DI\Container $container, array $options = NULL)
  7038. {
  7039. $session = new Nette\Http\Session($container->httpRequest, $container->httpResponse);
  7040. $session->setOptions((array) $options);
  7041. if (isset($options['expiration'])) {
  7042. $session->setExpiration($options['expiration']);
  7043. }
  7044. return $session;
  7045. }
  7046. static function createServiceUser(DI\Container $container)
  7047. {
  7048. $context = new DI\Container;
  7049. $context->addService('authenticator', function() use($container) {
  7050. return $container->authenticator;
  7051. });
  7052. $context->addService('authorizator', function() use($container) {
  7053. return $container->authorizator;
  7054. });
  7055. $context->addService('session', $container->session);
  7056. return new Nette\Http\User($context);
  7057. }
  7058. static function createServiceCacheStorage(DI\Container $container)
  7059. {
  7060. if (!isset($container->params['tempDir'])) {
  7061. throw new Nette\InvalidStateException("Service cacheStorage requires that parameter 'tempDir' contains path to temporary directory.");
  7062. }
  7063. $dir = $container->expand('%tempDir%/cache');
  7064. umask(0000);
  7065. @mkdir($dir, 0777);
  7066. return new Nette\Caching\Storages\FileStorage($dir, $container->cacheJournal);
  7067. }
  7068. static function createServiceTemplateCacheStorage(DI\Container $container)
  7069. {
  7070. if (!isset($container->params['tempDir'])) {
  7071. throw new Nette\InvalidStateException("Service templateCacheStorage requires that parameter 'tempDir' contains path to temporary directory.");
  7072. }
  7073. $dir = $container->expand('%tempDir%/cache');
  7074. umask(0000);
  7075. @mkdir($dir, 0777);
  7076. return new Nette\Caching\Storages\PhpFileStorage($dir);
  7077. }
  7078. static function createServiceCacheJournal(DI\Container $container)
  7079. {
  7080. return new Nette\Caching\Storages\FileJournal($container->params['tempDir']);
  7081. }
  7082. static function createServiceMailer(DI\Container $container, array $options = NULL)
  7083. {
  7084. if (empty($options['smtp'])) {
  7085. return new Nette\Mail\SendmailMailer;
  7086. } else {
  7087. return new Nette\Mail\SmtpMailer($options);
  7088. }
  7089. }
  7090. static function createServiceRobotLoader(DI\Container $container, array $options = NULL)
  7091. {
  7092. $loader = new Nette\Loaders\RobotLoader;
  7093. $loader->autoRebuild = isset($options['autoRebuild']) ? $options['autoRebuild'] : !$container->params['productionMode'];
  7094. $loader->setCacheStorage($container->cacheStorage);
  7095. if (isset($options['directory'])) {
  7096. $loader->addDirectory($options['directory']);
  7097. } else {
  7098. foreach (array('appDir', 'libsDir') as $var) {
  7099. if (isset($container->params[$var])) {
  7100. $loader->addDirectory($container->params[$var]);
  7101. }
  7102. }
  7103. }
  7104. $loader->register();
  7105. return $loader;
  7106. }
  7107. static function preloadEnvironment(DI\Container $container)
  7108. {
  7109. $code = '';
  7110. $dir = $container->expand('%tempDir%/cache');
  7111. umask(0000);
  7112. @mkdir($dir, 0777);
  7113. $uniq = uniqid('_', TRUE);
  7114. umask(0000);
  7115. if (!@mkdir("$dir/$uniq", 0777)) {
  7116. throw new Nette\InvalidStateException("Unable to write to directory '$dir'. Make this directory writable.");
  7117. }
  7118. $useDirs = @file_put_contents("$dir/$uniq/_", '') !== FALSE;
  7119. @unlink("$dir/$uniq/_");
  7120. @rmdir("$dir/$uniq");
  7121. $code .= self::generateCode('Nette\Caching\Storages\FileStorage::$useDirectories = ?', $useDirs);
  7122. return $code;
  7123. }
  7124. }
  7125. class DateTime extends \DateTime
  7126. {
  7127. const MINUTE = 60;
  7128. const HOUR = 3600;
  7129. const DAY = 86400;
  7130. const WEEK = 604800;
  7131. const MONTH = 2629800;
  7132. const YEAR = 31557600;
  7133. static function from($time)
  7134. {
  7135. if ($time instanceof \DateTime) {
  7136. return clone $time;
  7137. } elseif (is_numeric($time)) {
  7138. if ($time <= self::YEAR) {
  7139. $time += time();
  7140. }
  7141. return new static(date('Y-m-d H:i:s', $time));
  7142. } else {
  7143. return new static($time);
  7144. }
  7145. }
  7146. }
  7147. final class Environment
  7148. {
  7149. const DEVELOPMENT = 'development',
  7150. PRODUCTION = 'production',
  7151. CONSOLE = 'console';
  7152. private static $configurator;
  7153. private static $context;
  7154. final function __construct()
  7155. {
  7156. throw new StaticClassException;
  7157. }
  7158. static function setConfigurator(Configurator $configurator)
  7159. {
  7160. self::$configurator = $configurator;
  7161. }
  7162. static function getConfigurator()
  7163. {
  7164. if (self::$configurator === NULL) {
  7165. self::$configurator = Configurator::$instance ?: new Configurator;
  7166. }
  7167. return self::$configurator;
  7168. }
  7169. static function isConsole()
  7170. {
  7171. return self::getContext()->params['consoleMode'];
  7172. }
  7173. static function isProduction()
  7174. {
  7175. return self::getContext()->params['productionMode'];
  7176. }
  7177. static function setProductionMode($value = TRUE)
  7178. {
  7179. self::getContext()->params['productionMode'] = (bool) $value;
  7180. }
  7181. static function setVariable($name, $value, $expand = TRUE)
  7182. {
  7183. if ($expand && is_string($value)) {
  7184. $value = self::getContext()->expand($value);
  7185. }
  7186. self::getContext()->params[$name] = $value;
  7187. }
  7188. static function getVariable($name, $default = NULL)
  7189. {
  7190. if (isset(self::getContext()->params[$name])) {
  7191. return self::getContext()->params[$name];
  7192. } elseif (func_num_args() > 1) {
  7193. return $default;
  7194. } else {
  7195. throw new InvalidStateException("Unknown environment variable '$name'.");
  7196. }
  7197. }
  7198. static function getVariables()
  7199. {
  7200. return self::getContext()->params;
  7201. }
  7202. static function expand($s)
  7203. {
  7204. return self::getContext()->expand($s);
  7205. }
  7206. static function setContext(DI\IContainer $context)
  7207. {
  7208. self::$context = $context;
  7209. }
  7210. static function getContext()
  7211. {
  7212. if (self::$context === NULL) {
  7213. self::$context = self::getConfigurator()->getContainer();
  7214. }
  7215. return self::$context;
  7216. }
  7217. static function getService($name)
  7218. {
  7219. return self::getContext()->getService($name);
  7220. }
  7221. static function __callStatic($name, $args)
  7222. {
  7223. if (!$args && strncasecmp($name, 'get', 3) === 0) {
  7224. return self::getContext()->getService(lcfirst(substr($name, 3)));
  7225. } else {
  7226. throw new MemberAccessException("Call to undefined static method Nette\\Environment::$name().");
  7227. }
  7228. }
  7229. static function getHttpRequest()
  7230. {
  7231. return self::getContext()->httpRequest;
  7232. }
  7233. static function getHttpContext()
  7234. {
  7235. return self::getContext()->httpContext;
  7236. }
  7237. static function getHttpResponse()
  7238. {
  7239. return self::getContext()->httpResponse;
  7240. }
  7241. static function getApplication()
  7242. {
  7243. return self::getContext()->application;
  7244. }
  7245. static function getUser()
  7246. {
  7247. return self::getContext()->user;
  7248. }
  7249. static function getRobotLoader()
  7250. {
  7251. return self::getContext()->robotLoader;
  7252. }
  7253. static function getCache($namespace = '')
  7254. {
  7255. return new Caching\Cache(self::getContext()->cacheStorage, $namespace);
  7256. }
  7257. static function getSession($namespace = NULL)
  7258. {
  7259. return $namespace === NULL
  7260. ? self::getContext()->session
  7261. : self::getContext()->session->getSection($namespace);
  7262. }
  7263. static function loadConfig($file = NULL, $section = NULL)
  7264. {
  7265. self::getConfigurator()->loadConfig($file, $section);
  7266. return self::getContext()->params;
  7267. }
  7268. static function getConfig($key = NULL, $default = NULL)
  7269. {
  7270. $params = self::getContext()->params;
  7271. if (func_num_args()) {
  7272. return isset($params[$key]) ? $params[$key] : $default;
  7273. } else {
  7274. return $params;
  7275. }
  7276. }
  7277. }
  7278. final class Framework
  7279. {
  7280. const NAME = 'Nette Framework',
  7281. VERSION = '2.0-beta',
  7282. REVISION = '648b258 released on 2011-06-13';
  7283. public static $iAmUsingBadHost = FALSE;
  7284. final function __construct()
  7285. {
  7286. throw new StaticClassException;
  7287. }
  7288. }
  7289. class Image extends Object
  7290. {
  7291. const ENLARGE = 1;
  7292. const STRETCH = 2;
  7293. const FIT = 0;
  7294. const FILL = 4;
  7295. const JPEG = IMAGETYPE_JPEG,
  7296. PNG = IMAGETYPE_PNG,
  7297. GIF = IMAGETYPE_GIF;
  7298. const EMPTY_GIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
  7299. private $image;
  7300. static function rgb($red, $green, $blue, $transparency = 0)
  7301. {
  7302. return array(
  7303. 'red' => max(0, min(255, (int) $red)),
  7304. 'green' => max(0, min(255, (int) $green)),
  7305. 'blue' => max(0, min(255, (int) $blue)),
  7306. 'alpha' => max(0, min(127, (int) $transparency)),
  7307. );
  7308. }
  7309. static function fromFile($file, & $format = NULL)
  7310. {
  7311. if (!extension_loaded('gd')) {
  7312. throw new NotSupportedException("PHP extension GD is not loaded.");
  7313. }
  7314. $info = @getimagesize($file);
  7315. switch ($format = $info[2]) {
  7316. case self::JPEG:
  7317. return new static(imagecreatefromjpeg($file));
  7318. case self::PNG:
  7319. return new static(imagecreatefrompng($file));
  7320. case self::GIF:
  7321. return new static(imagecreatefromgif($file));
  7322. default:
  7323. throw new UnknownImageFileException("Unknown image type or file '$file' not found.");
  7324. }
  7325. }
  7326. static function getFormatFromString($s)
  7327. {
  7328. $types = array('image/jpeg' => self::JPEG, 'image/gif' => self::GIF, 'image/png' => self::PNG);
  7329. $type = Utils\MimeTypeDetector::fromString($s);
  7330. return isset($types[$type]) ? $types[$type] : NULL;
  7331. }
  7332. static function fromString($s, & $format = NULL)
  7333. {
  7334. if (!extension_loaded('gd')) {
  7335. throw new NotSupportedException("PHP extension GD is not loaded.");
  7336. }
  7337. $format = static::getFormatFromString($s);
  7338. return new static(imagecreatefromstring($s));
  7339. }
  7340. static function fromBlank($width, $height, $color = NULL)
  7341. {
  7342. if (!extension_loaded('gd')) {
  7343. throw new NotSupportedException("PHP extension GD is not loaded.");
  7344. }
  7345. $width = (int) $width;
  7346. $height = (int) $height;
  7347. if ($width < 1 || $height < 1) {
  7348. throw new InvalidArgumentException('Image width and height must be greater than zero.');
  7349. }
  7350. $image = imagecreatetruecolor($width, $height);
  7351. if (is_array($color)) {
  7352. $color += array('alpha' => 0);
  7353. $color = imagecolorallocatealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
  7354. imagealphablending($image, FALSE);
  7355. imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
  7356. imagealphablending($image, TRUE);
  7357. }
  7358. return new static($image);
  7359. }
  7360. function __construct($image)
  7361. {
  7362. $this->setImageResource($image);
  7363. imagesavealpha($image, TRUE);
  7364. }
  7365. function getWidth()
  7366. {
  7367. return imagesx($this->image);
  7368. }
  7369. function getHeight()
  7370. {
  7371. return imagesy($this->image);
  7372. }
  7373. protected function setImageResource($image)
  7374. {
  7375. if (!is_resource($image) || get_resource_type($image) !== 'gd') {
  7376. throw new InvalidArgumentException('Image is not valid.');
  7377. }
  7378. $this->image = $image;
  7379. return $this;
  7380. }
  7381. function getImageResource()
  7382. {
  7383. return $this->image;
  7384. }
  7385. function resize($width, $height, $flags = self::FIT)
  7386. {
  7387. list($newWidth, $newHeight) = self::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags);
  7388. if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) {
  7389. $newImage = self::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
  7390. imagecopyresampled(
  7391. $newImage, $this->getImageResource(),
  7392. 0, 0, 0, 0,
  7393. $newWidth, $newHeight, $this->getWidth(), $this->getHeight()
  7394. );
  7395. $this->image = $newImage;
  7396. }
  7397. if ($width < 0 || $height < 0) {
  7398. $newImage = self::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
  7399. imagecopyresampled(
  7400. $newImage, $this->getImageResource(),
  7401. 0, 0, $width < 0 ? $newWidth - 1 : 0, $height < 0 ? $newHeight - 1 : 0,
  7402. $newWidth, $newHeight, $width < 0 ? -$newWidth : $newWidth, $height < 0 ? -$newHeight : $newHeight
  7403. );
  7404. $this->image = $newImage;
  7405. }
  7406. return $this;
  7407. }
  7408. static function calculateSize($srcWidth, $srcHeight, $newWidth, $newHeight, $flags = self::FIT)
  7409. {
  7410. if (substr($newWidth, -1) === '%') {
  7411. $newWidth = round($srcWidth / 100 * abs($newWidth));
  7412. $flags |= self::ENLARGE;
  7413. $percents = TRUE;
  7414. } else {
  7415. $newWidth = (int) abs($newWidth);
  7416. }
  7417. if (substr($newHeight, -1) === '%') {
  7418. $newHeight = round($srcHeight / 100 * abs($newHeight));
  7419. $flags |= empty($percents) ? self::ENLARGE : self::STRETCH;
  7420. } else {
  7421. $newHeight = (int) abs($newHeight);
  7422. }
  7423. if ($flags & self::STRETCH) {
  7424. if (empty($newWidth) || empty($newHeight)) {
  7425. throw new InvalidArgumentException('For stretching must be both width and height specified.');
  7426. }
  7427. if (($flags & self::ENLARGE) === 0) {
  7428. $newWidth = round($srcWidth * min(1, $newWidth / $srcWidth));
  7429. $newHeight = round($srcHeight * min(1, $newHeight / $srcHeight));
  7430. }
  7431. } else {
  7432. if (empty($newWidth) && empty($newHeight)) {
  7433. throw new InvalidArgumentException('At least width or height must be specified.');
  7434. }
  7435. $scale = array();
  7436. if ($newWidth > 0) {
  7437. $scale[] = $newWidth / $srcWidth;
  7438. }
  7439. if ($newHeight > 0) {
  7440. $scale[] = $newHeight / $srcHeight;
  7441. }
  7442. if ($flags & self::FILL) {
  7443. $scale = array(max($scale));
  7444. }
  7445. if (($flags & self::ENLARGE) === 0) {
  7446. $scale[] = 1;
  7447. }
  7448. $scale = min($scale);
  7449. $newWidth = round($srcWidth * $scale);
  7450. $newHeight = round($srcHeight * $scale);
  7451. }
  7452. return array(max((int) $newWidth, 1), max((int) $newHeight, 1));
  7453. }
  7454. function crop($left, $top, $width, $height)
  7455. {
  7456. list($left, $top, $width, $height) = self::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height);
  7457. $newImage = self::fromBlank($width, $height, self::RGB(0, 0, 0, 127))->getImageResource();
  7458. imagecopy($newImage, $this->getImageResource(), 0, 0, $left, $top, $width, $height);
  7459. $this->image = $newImage;
  7460. return $this;
  7461. }
  7462. static function calculateCutout($srcWidth, $srcHeight, $left, $top, $newWidth, $newHeight)
  7463. {
  7464. if (substr($newWidth, -1) === '%') {
  7465. $newWidth = round($srcWidth / 100 * $newWidth);
  7466. }
  7467. if (substr($newHeight, -1) === '%') {
  7468. $newHeight = round($srcHeight / 100 * $newHeight);
  7469. }
  7470. if (substr($left, -1) === '%') {
  7471. $left = round(($srcWidth - $newWidth) / 100 * $left);
  7472. }
  7473. if (substr($top, -1) === '%') {
  7474. $top = round(($srcHeight - $newHeight) / 100 * $top);
  7475. }
  7476. if ($left < 0) {
  7477. $newWidth += $left; $left = 0;
  7478. }
  7479. if ($top < 0) {
  7480. $newHeight += $top; $top = 0;
  7481. }
  7482. $newWidth = min((int) $newWidth, $srcWidth - $left);
  7483. $newHeight = min((int) $newHeight, $srcHeight - $top);
  7484. return array($left, $top, $newWidth, $newHeight);
  7485. }
  7486. function sharpen()
  7487. {
  7488. imageconvolution($this->getImageResource(), array(
  7489. array( -1, -1, -1 ),
  7490. array( -1, 24, -1 ),
  7491. array( -1, -1, -1 ),
  7492. ), 16, 0);
  7493. return $this;
  7494. }
  7495. function place(Image $image, $left = 0, $top = 0, $opacity = 100)
  7496. {
  7497. $opacity = max(0, min(100, (int) $opacity));
  7498. if (substr($left, -1) === '%') {
  7499. $left = round(($this->getWidth() - $image->getWidth()) / 100 * $left);
  7500. }
  7501. if (substr($top, -1) === '%') {
  7502. $top = round(($this->getHeight() - $image->getHeight()) / 100 * $top);
  7503. }
  7504. if ($opacity === 100) {
  7505. imagecopy(
  7506. $this->getImageResource(), $image->getImageResource(),
  7507. $left, $top, 0, 0, $image->getWidth(), $image->getHeight()
  7508. );
  7509. } elseif ($opacity <> 0) {
  7510. imagecopymerge(
  7511. $this->getImageResource(), $image->getImageResource(),
  7512. $left, $top, 0, 0, $image->getWidth(), $image->getHeight(),
  7513. $opacity
  7514. );
  7515. }
  7516. return $this;
  7517. }
  7518. function save($file = NULL, $quality = NULL, $type = NULL)
  7519. {
  7520. if ($type === NULL) {
  7521. switch (strtolower(pathinfo($file, PATHINFO_EXTENSION))) {
  7522. case 'jpg':
  7523. case 'jpeg':
  7524. $type = self::JPEG;
  7525. break;
  7526. case 'png':
  7527. $type = self::PNG;
  7528. break;
  7529. case 'gif':
  7530. $type = self::GIF;
  7531. }
  7532. }
  7533. switch ($type) {
  7534. case self::JPEG:
  7535. $quality = $quality === NULL ? 85 : max(0, min(100, (int) $quality));
  7536. return imagejpeg($this->getImageResource(), $file, $quality);
  7537. case self::PNG:
  7538. $quality = $quality === NULL ? 9 : max(0, min(9, (int) $quality));
  7539. return imagepng($this->getImageResource(), $file, $quality);
  7540. case self::GIF:
  7541. return $file === NULL ? imagegif($this->getImageResource()) : imagegif($this->getImageResource(), $file);
  7542. default:
  7543. throw new InvalidArgumentException("Unsupported image type.");
  7544. }
  7545. }
  7546. function toString($type = self::JPEG, $quality = NULL)
  7547. {
  7548. ob_start();
  7549. $this->save(NULL, $quality, $type);
  7550. return ob_get_clean();
  7551. }
  7552. function __toString()
  7553. {
  7554. try {
  7555. return $this->toString();
  7556. } catch (\Exception $e) {
  7557. Diagnostics\Debugger::toStringException($e);
  7558. }
  7559. }
  7560. function send($type = self::JPEG, $quality = NULL)
  7561. {
  7562. if ($type !== self::GIF && $type !== self::PNG && $type !== self::JPEG) {
  7563. throw new InvalidArgumentException("Unsupported image type.");
  7564. }
  7565. header('Content-Type: ' . image_type_to_mime_type($type));
  7566. return $this->save(NULL, $quality, $type);
  7567. }
  7568. function __call($name, $args)
  7569. {
  7570. $function = 'image' . $name;
  7571. if (function_exists($function)) {
  7572. foreach ($args as $key => $value) {
  7573. if ($value instanceof self) {
  7574. $args[$key] = $value->getImageResource();
  7575. } elseif (is_array($value) && isset($value['red'])) {
  7576. $args[$key] = imagecolorallocatealpha(
  7577. $this->getImageResource(),
  7578. $value['red'], $value['green'], $value['blue'], $value['alpha']
  7579. );
  7580. }
  7581. }
  7582. array_unshift($args, $this->getImageResource());
  7583. $res = call_user_func_array($function, $args);
  7584. return is_resource($res) && get_resource_type($res) === 'gd' ? $this->setImageResource($res) : $res;
  7585. }
  7586. return parent::__call($name, $args);
  7587. }
  7588. }
  7589. class UnknownImageFileException extends \Exception
  7590. {
  7591. }
  7592. final class ObjectMixin
  7593. {
  7594. private static $methods;
  7595. final function __construct()
  7596. {
  7597. throw new StaticClassException;
  7598. }
  7599. static function call($_this, $name, $args)
  7600. {
  7601. $class = new Reflection\ClassType($_this);
  7602. if ($name === '') {
  7603. throw new MemberAccessException("Call to class '$class->name' method without name.");
  7604. }
  7605. if ($class->hasEventProperty($name)) {
  7606. if (is_array($list = $_this->$name) || $list instanceof \Traversable) {
  7607. foreach ($list as $handler) {
  7608. callback($handler)->invokeArgs($args);
  7609. }
  7610. }
  7611. return NULL;
  7612. }
  7613. if ($cb = $class->getExtensionMethod($name)) {
  7614. array_unshift($args, $_this);
  7615. return $cb->invokeArgs($args);
  7616. }
  7617. throw new MemberAccessException("Call to undefined method $class->name::$name().");
  7618. }
  7619. static function callStatic($class, $name, $args)
  7620. {
  7621. throw new MemberAccessException("Call to undefined static method $class::$name().");
  7622. }
  7623. static function & get($_this, $name)
  7624. {
  7625. $class = get_class($_this);
  7626. if ($name === '') {
  7627. throw new MemberAccessException("Cannot read a class '$class' property without name.");
  7628. }
  7629. if (!isset(self::$methods[$class])) {
  7630. self::$methods[$class] = array_flip(get_class_methods($class));
  7631. }
  7632. $name[0] = $name[0] & "\xDF";
  7633. $m = 'get' . $name;
  7634. if (isset(self::$methods[$class][$m])) {
  7635. $val = $_this->$m();
  7636. return $val;
  7637. }
  7638. $m = 'is' . $name;
  7639. if (isset(self::$methods[$class][$m])) {
  7640. $val = $_this->$m();
  7641. return $val;
  7642. }
  7643. $type = isset(self::$methods[$class]['set' . $name]) ? 'a write-only' : 'an undeclared';
  7644. $name = func_get_arg(1);
  7645. throw new MemberAccessException("Cannot read $type property $class::\$$name.");
  7646. }
  7647. static function set($_this, $name, $value)
  7648. {
  7649. $class = get_class($_this);
  7650. if ($name === '') {
  7651. throw new MemberAccessException("Cannot write to a class '$class' property without name.");
  7652. }
  7653. if (!isset(self::$methods[$class])) {
  7654. self::$methods[$class] = array_flip(get_class_methods($class));
  7655. }
  7656. $name[0] = $name[0] & "\xDF";
  7657. $m = 'set' . $name;
  7658. if (isset(self::$methods[$class][$m])) {
  7659. $_this->$m($value);
  7660. return;
  7661. }
  7662. $type = isset(self::$methods[$class]['get' . $name]) || isset(self::$methods[$class]['is' . $name])
  7663. ? 'a read-only' : 'an undeclared';
  7664. $name = func_get_arg(1);
  7665. throw new MemberAccessException("Cannot write to $type property $class::\$$name.");
  7666. }
  7667. static function remove($_this, $name)
  7668. {
  7669. $class = get_class($_this);
  7670. throw new MemberAccessException("Cannot unset the property $class::\$$name.");
  7671. }
  7672. static function has($_this, $name)
  7673. {
  7674. if ($name === '') {
  7675. return FALSE;
  7676. }
  7677. $class = get_class($_this);
  7678. if (!isset(self::$methods[$class])) {
  7679. self::$methods[$class] = array_flip(get_class_methods($class));
  7680. }
  7681. $name[0] = $name[0] & "\xDF";
  7682. return isset(self::$methods[$class]['get' . $name]) || isset(self::$methods[$class]['is' . $name]);
  7683. }
  7684. }
  7685. }
  7686. namespace Nette\ComponentModel {
  7687. use Nette;
  7688. class RecursiveComponentIterator extends \RecursiveArrayIterator implements \Countable
  7689. {
  7690. function hasChildren()
  7691. {
  7692. return $this->current() instanceof IContainer;
  7693. }
  7694. function getChildren()
  7695. {
  7696. return $this->current()->getComponents();
  7697. }
  7698. function count()
  7699. {
  7700. return iterator_count($this);
  7701. }
  7702. }
  7703. }
  7704. namespace Nette\Config {
  7705. use Nette;
  7706. class Config
  7707. {
  7708. private static $extensions = array(
  7709. 'ini' => 'Nette\Config\IniAdapter',
  7710. 'neon' => 'Nette\Config\NeonAdapter',
  7711. );
  7712. final function __construct()
  7713. {
  7714. throw new Nette\StaticClassException;
  7715. }
  7716. static function registerExtension($extension, $class)
  7717. {
  7718. if (!class_exists($class)) {
  7719. throw new Nette\InvalidArgumentException("Class '$class' was not found.");
  7720. }
  7721. if (!Nette\Reflection\ClassType::from($class)->implementsInterface('Nette\Config\IAdapter')) {
  7722. throw new Nette\InvalidArgumentException("Configuration adapter '$class' is not Nette\\Config\\IAdapter implementor.");
  7723. }
  7724. self::$extensions[strtolower($extension)] = $class;
  7725. }
  7726. static function fromFile($file, $section = NULL)
  7727. {
  7728. $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
  7729. if (!isset(self::$extensions[$extension])) {
  7730. throw new Nette\InvalidArgumentException("Unknown file extension '$file'.");
  7731. }
  7732. $data = call_user_func(array(self::$extensions[$extension], 'load'), $file, $section);
  7733. if ($section) {
  7734. if (!isset($data[$section]) || !is_array($data[$section])) {
  7735. throw new Nette\InvalidStateException("There is not section [$section] in file '$file'.");
  7736. }
  7737. $data = $data[$section];
  7738. }
  7739. return $data;
  7740. }
  7741. static function save($config, $file)
  7742. {
  7743. $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
  7744. if (!isset(self::$extensions[$extension])) {
  7745. throw new Nette\InvalidArgumentException("Unknown file extension '$file'.");
  7746. }
  7747. return call_user_func(array(self::$extensions[$extension], 'save'), $config, $file);
  7748. }
  7749. }
  7750. final class IniAdapter implements IAdapter
  7751. {
  7752. public static $keySeparator = '.';
  7753. public static $sectionSeparator = ' < ';
  7754. public static $rawSection = '!';
  7755. final function __construct()
  7756. {
  7757. throw new Nette\StaticClassException;
  7758. }
  7759. static function load($file)
  7760. {
  7761. if (!is_file($file) || !is_readable($file)) {
  7762. throw new Nette\FileNotFoundException("File '$file' is missing or is not readable.");
  7763. }
  7764. Nette\Diagnostics\Debugger::tryError();
  7765. $ini = parse_ini_file($file, TRUE);
  7766. if (Nette\Diagnostics\Debugger::catchError($e)) {
  7767. throw new Nette\InvalidStateException('parse_ini_file(): ' . $e->getMessage(), 0, $e);
  7768. }
  7769. $separator = trim(self::$sectionSeparator);
  7770. $data = array();
  7771. foreach ($ini as $secName => $secData) {
  7772. if (is_array($secData)) {
  7773. if (substr($secName, -1) === self::$rawSection) {
  7774. $secName = substr($secName, 0, -1);
  7775. } elseif (self::$keySeparator) {
  7776. $tmp = array();
  7777. foreach ($secData as $key => $val) {
  7778. $cursor = & $tmp;
  7779. foreach (explode(self::$keySeparator, $key) as $part) {
  7780. if (!isset($cursor[$part]) || is_array($cursor[$part])) {
  7781. $cursor = & $cursor[$part];
  7782. } else {
  7783. throw new Nette\InvalidStateException("Invalid key '$key' in section [$secName] in file '$file'.");
  7784. }
  7785. }
  7786. $cursor = $val;
  7787. }
  7788. $secData = $tmp;
  7789. }
  7790. $parts = $separator ? explode($separator, strtr($secName, ':', $separator)) : array($secName);
  7791. if (count($parts) > 1) {
  7792. $parent = trim($parts[1]);
  7793. if (!isset($data[$parent]) || !is_array($data[$parent])) {
  7794. throw new Nette\InvalidStateException("Missing parent section [$parent] in file '$file'.");
  7795. }
  7796. $secData = Nette\Utils\Arrays::mergeTree($secData, $data[$parent]);
  7797. $secName = trim($parts[0]);
  7798. if ($secName === '') {
  7799. throw new Nette\InvalidStateException("Invalid empty section name in file '$file'.");
  7800. }
  7801. }
  7802. }
  7803. if (self::$keySeparator) {
  7804. $cursor = & $data;
  7805. foreach (explode(self::$keySeparator, $secName) as $part) {
  7806. if (!isset($cursor[$part]) || is_array($cursor[$part])) {
  7807. $cursor = & $cursor[$part];
  7808. } else {
  7809. throw new Nette\InvalidStateException("Invalid section [$secName] in file '$file'.");
  7810. }
  7811. }
  7812. } else {
  7813. $cursor = & $data[$secName];
  7814. }
  7815. if (is_array($secData) && is_array($cursor)) {
  7816. $secData = Nette\Utils\Arrays::mergeTree($secData, $cursor);
  7817. }
  7818. $cursor = $secData;
  7819. }
  7820. return $data;
  7821. }
  7822. static function save($config, $file)
  7823. {
  7824. $output = array();
  7825. $output[] = '; generated by Nette';
  7826. $output[] = '';
  7827. foreach ($config as $secName => $secData) {
  7828. if (!(is_array($secData) || $secData instanceof \Traversable)) {
  7829. throw new Nette\InvalidStateException("Invalid section '$secName'.");
  7830. }
  7831. $output[] = "[$secName]";
  7832. self::build($secData, $output, '');
  7833. $output[] = '';
  7834. }
  7835. if (!file_put_contents($file, implode(PHP_EOL, $output))) {
  7836. throw new Nette\IOException("Cannot write file '$file'.");
  7837. }
  7838. }
  7839. private static function build($input, & $output, $prefix)
  7840. {
  7841. foreach ($input as $key => $val) {
  7842. if (is_array($val) || $val instanceof \Traversable) {
  7843. self::build($val, $output, $prefix . $key . self::$keySeparator);
  7844. } elseif (is_bool($val)) {
  7845. $output[] = "$prefix$key = " . ($val ? 'true' : 'false');
  7846. } elseif (is_numeric($val)) {
  7847. $output[] = "$prefix$key = $val";
  7848. } elseif (is_string($val)) {
  7849. $output[] = "$prefix$key = \"$val\"";
  7850. } else {
  7851. throw new Nette\InvalidArgumentException("The '$prefix$key' item must be scalar or array, " . gettype($val) ." given.");
  7852. }
  7853. }
  7854. }
  7855. }
  7856. use Nette\Utils\Neon;
  7857. final class NeonAdapter implements IAdapter
  7858. {
  7859. public static $sectionSeparator = ' < ';
  7860. final function __construct()
  7861. {
  7862. throw new Nette\StaticClassException;
  7863. }
  7864. static function load($file)
  7865. {
  7866. if (!is_file($file) || !is_readable($file)) {
  7867. throw new Nette\FileNotFoundException("File '$file' is missing or is not readable.");
  7868. }
  7869. $neon = Neon::decode(file_get_contents($file));
  7870. $separator = trim(self::$sectionSeparator);
  7871. $data = array();
  7872. foreach ($neon as $secName => $secData) {
  7873. if ($secData === NULL) {
  7874. $secData = array();
  7875. }
  7876. if (is_array($secData)) {
  7877. $parts = $separator ? explode($separator, $secName) : array($secName);
  7878. if (count($parts) > 1) {
  7879. $parent = trim($parts[1]);
  7880. if (!isset($data[$parent]) || !is_array($data[$parent])) {
  7881. throw new Nette\InvalidStateException("Missing parent section '$parent' in file '$file'.");
  7882. }
  7883. $secData = Nette\Utils\Arrays::mergeTree($secData, $data[$parent]);
  7884. $secName = trim($parts[0]);
  7885. if ($secName === '') {
  7886. throw new Nette\InvalidStateException("Invalid empty section name in file '$file'.");
  7887. }
  7888. }
  7889. }
  7890. $data[$secName] = $secData;
  7891. }
  7892. return $data;
  7893. }
  7894. static function save($config, $file)
  7895. {
  7896. if (!file_put_contents($file, "# generated by Nette\n\n" . Neon::encode($config, Neon::BLOCK))) {
  7897. throw new Nette\IOException("Cannot write file '$file'.");
  7898. }
  7899. }
  7900. }
  7901. }
  7902. namespace Nette\Database {
  7903. use Nette;use Nette\ObjectMixin;use PDO;
  7904. if (class_exists('PDO')){ class Connection extends PDO
  7905. {
  7906. private $driver;
  7907. private $preprocessor;
  7908. public $databaseReflection;
  7909. public $cache;
  7910. public $substitutions = array();
  7911. public $onQuery;
  7912. function __construct($dsn, $username = NULL, $password = NULL, array $options = NULL)
  7913. {
  7914. parent::__construct($dsn, $username, $password, $options);
  7915. $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  7916. $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Nette\Database\Statement', array($this)));
  7917. $class = 'Nette\Database\Drivers\\' . $this->getAttribute(PDO::ATTR_DRIVER_NAME) . 'Driver';
  7918. if (class_exists($class)) {
  7919. $this->driver = new $class($this, (array) $options);
  7920. }
  7921. $this->preprocessor = new SqlPreprocessor($this);
  7922. $this->databaseReflection = new Reflection\DatabaseReflection;
  7923. Diagnostics\ConnectionPanel::initialize($this);
  7924. }
  7925. function getSupplementalDriver()
  7926. {
  7927. return $this->driver;
  7928. }
  7929. function query($statement)
  7930. {
  7931. $args = func_get_args();
  7932. return $this->queryArgs(array_shift($args), $args);
  7933. }
  7934. function exec($statement)
  7935. {
  7936. $args = func_get_args();
  7937. return $this->queryArgs(array_shift($args), $args)->rowCount();
  7938. }
  7939. function queryArgs($statement, $params)
  7940. {
  7941. foreach ($params as $value) {
  7942. if (is_array($value) || is_object($value)) {
  7943. $need = TRUE; break;
  7944. }
  7945. }
  7946. if (isset($need) || strpos($statement, ':') !== FALSE && $this->preprocessor !== NULL) {
  7947. list($statement, $params) = $this->preprocessor->process($statement, $params);
  7948. }
  7949. return $this->prepare($statement)->execute($params);
  7950. }
  7951. function fetch($args)
  7952. {
  7953. $args = func_get_args();
  7954. return $this->queryArgs(array_shift($args), $args)->fetch();
  7955. }
  7956. function fetchColumn($args)
  7957. {
  7958. $args = func_get_args();
  7959. return $this->queryArgs(array_shift($args), $args)->fetchColumn();
  7960. }
  7961. function fetchPairs($args)
  7962. {
  7963. $args = func_get_args();
  7964. return $this->queryArgs(array_shift($args), $args)->fetchPairs();
  7965. }
  7966. function fetchAll($args)
  7967. {
  7968. $args = func_get_args();
  7969. return $this->queryArgs(array_shift($args), $args)->fetchAll();
  7970. }
  7971. function table($table)
  7972. {
  7973. return new Table\Selection($table, $this);
  7974. }
  7975. function loadFile($file)
  7976. {
  7977. @set_time_limit(0);
  7978. $handle = @fopen($file, 'r');
  7979. if (!$handle) {
  7980. throw new Nette\FileNotFoundException("Cannot open file '$file'.");
  7981. }
  7982. $count = 0;
  7983. $sql = '';
  7984. while (!feof($handle)) {
  7985. $s = fgets($handle);
  7986. $sql .= $s;
  7987. if (substr(rtrim($s), -1) === ';') {
  7988. parent::exec($sql);
  7989. $sql = '';
  7990. $count++;
  7991. }
  7992. }
  7993. fclose($handle);
  7994. return $count;
  7995. }
  7996. static function highlightSql($sql)
  7997. {
  7998. static $keywords1 = 'SELECT|UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
  7999. static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|TRUE|FALSE';
  8000. $sql = " $sql ";
  8001. $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
  8002. $sql = preg_replace('#[ \t]{2,}#', " ", $sql);
  8003. $sql = wordwrap($sql, 100);
  8004. $sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
  8005. $sql = htmlSpecialChars($sql);
  8006. $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", function($matches) {
  8007. if (!empty($matches[1]))
  8008. return '<em style="color:gray">' . $matches[1] . '</em>';
  8009. if (!empty($matches[2]))
  8010. return '<strong style="color:red">' . $matches[2] . '</strong>';
  8011. if (!empty($matches[3]))
  8012. return '<strong style="color:blue">' . $matches[3] . '</strong>';
  8013. if (!empty($matches[4]))
  8014. return '<strong style="color:green">' . $matches[4] . '</strong>';
  8015. }, $sql);
  8016. return '<pre class="dump">' . trim($sql) . "</pre>\n";
  8017. }
  8018. static function getReflection()
  8019. {
  8020. return new Nette\Reflection\ClassType(get_called_class());
  8021. }
  8022. function __call($name, $args)
  8023. {
  8024. return ObjectMixin::call($this, $name, $args);
  8025. }
  8026. function &__get($name)
  8027. {
  8028. return ObjectMixin::get($this, $name);
  8029. }
  8030. function __set($name, $value)
  8031. {
  8032. return ObjectMixin::set($this, $name, $value);
  8033. }
  8034. function __isset($name)
  8035. {
  8036. return ObjectMixin::has($this, $name);
  8037. }
  8038. function __unset($name)
  8039. {
  8040. ObjectMixin::remove($this, $name);
  8041. }
  8042. }
  8043. }
  8044. }
  8045. namespace Nette\Database\Diagnostics {
  8046. use Nette;use Nette\Database\Connection;use Nette\Diagnostics\Debugger;
  8047. class ConnectionPanel extends Nette\Object implements Nette\Diagnostics\IBarPanel
  8048. {
  8049. static public $maxLength = 1000;
  8050. public $totalTime = 0;
  8051. public $queries = array();
  8052. public $name;
  8053. public $explain = TRUE;
  8054. public $disabled = FALSE;
  8055. static function initialize(Connection $connection)
  8056. {
  8057. if (!Debugger::$productionMode) {
  8058. $panel = new static;
  8059. $connection->onQuery[] = callback($panel, 'logQuery');
  8060. Debugger::$bar->addPanel($panel);
  8061. Debugger::$blueScreen->addPanel(callback($panel, 'renderException'), __CLASS__);
  8062. }
  8063. }
  8064. function logQuery(Nette\Database\Statement $result, array $params = NULL)
  8065. {
  8066. if ($this->disabled) {
  8067. return;
  8068. }
  8069. $source = NULL;
  8070. foreach (debug_backtrace(FALSE) as $row) {
  8071. if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], NETTE_DIR . DIRECTORY_SEPARATOR) !== 0) {
  8072. $source = array($row['file'], (int) $row['line']);
  8073. break;
  8074. }
  8075. }
  8076. $this->totalTime += $result->time;
  8077. $this->queries[] = array($result->queryString, $params, $result->time, $result->rowCount(), $result->getConnection(), $source);
  8078. }
  8079. function renderException($e)
  8080. {
  8081. if ($e instanceof \PDOException && isset($e->queryString)) {
  8082. return array(
  8083. 'tab' => 'SQL',
  8084. 'panel' => Connection::highlightSql($e->queryString),
  8085. );
  8086. }
  8087. }
  8088. function getTab()
  8089. {
  8090. return '<span title="Nette\\Database ' . htmlSpecialChars($this->name) . '">'
  8091. . '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEYSURBVBgZBcHPio5hGAfg6/2+R980k6wmJgsJ5U/ZOAqbSc2GnXOwUg7BESgLUeIQ1GSjLFnMwsKGGg1qxJRmPM97/1zXFAAAAEADdlfZzr26miup2svnelq7d2aYgt3rebl585wN6+K3I1/9fJe7O/uIePP2SypJkiRJ0vMhr55FLCA3zgIAOK9uQ4MS361ZOSX+OrTvkgINSjS/HIvhjxNNFGgQsbSmabohKDNoUGLohsls6BaiQIMSs2FYmnXdUsygQYmumy3Nhi6igwalDEOJEjPKP7CA2aFNK8Bkyy3fdNCg7r9/fW3jgpVJbDmy5+PB2IYp4MXFelQ7izPrhkPHB+P5/PjhD5gCgCenx+VR/dODEwD+A3T7nqbxwf1HAAAAAElFTkSuQmCC" />'
  8092. . count($this->queries) . ' queries'
  8093. . ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '')
  8094. . '</span>';
  8095. }
  8096. function getPanel()
  8097. {
  8098. $this->disabled = TRUE;
  8099. $s = '';
  8100. $h = 'htmlSpecialChars';
  8101. foreach ($this->queries as $i => $query) {
  8102. list($sql, $params, $time, $rows, $connection, $source) = $query;
  8103. $explain = NULL;
  8104. if ($this->explain && preg_match('#\s*SELECT\s#iA', $sql)) {
  8105. try {
  8106. $explain = $connection->queryArgs('EXPLAIN ' . $sql, $params)->fetchAll();
  8107. } catch (\PDOException $e) {}
  8108. }
  8109. $s .= '<tr><td>' . sprintf('%0.3f', $time * 1000);
  8110. if ($explain) {
  8111. static $counter;
  8112. $counter++;
  8113. $s .= "<br /><a href='#' class='nette-toggler' rel='#nette-DbConnectionPanel-row-$counter'>explain&nbsp;&#x25ba;</a>";
  8114. }
  8115. $s .= '</td><td class="nette-DbConnectionPanel-sql">' . Connection::highlightSql(Nette\Utils\Strings::truncate($sql, self::$maxLength));
  8116. if ($explain) {
  8117. $s .= "<table id='nette-DbConnectionPanel-row-$counter' class='nette-collapsed'><tr>";
  8118. foreach ($explain[0] as $col => $foo) {
  8119. $s .= "<th>{$h($col)}</th>";
  8120. }
  8121. $s .= "</tr>";
  8122. foreach ($explain as $row) {
  8123. $s .= "<tr>";
  8124. foreach ($row as $col) {
  8125. $s .= "<td>{$h($col)}</td>";
  8126. }
  8127. $s .= "</tr>";
  8128. }
  8129. $s .= "</table>";
  8130. }
  8131. if ($source) {
  8132. $s .= Nette\Diagnostics\Helpers::editorLink($source[0], $source[1])->class('nette-DbConnectionPanel-source');
  8133. }
  8134. $s .= '</td><td>';
  8135. foreach ($params as $param) {
  8136. $s .= Debugger::dump($param, TRUE);
  8137. }
  8138. $s .= '</td><td>' . $rows . '</td></tr>';
  8139. }
  8140. return empty($this->queries) ? '' :
  8141. '<style> #nette-debug td.nette-DbConnectionPanel-sql { background: white !important }
  8142. #nette-debug .nette-DbConnectionPanel-source { color: #BBB !important }
  8143. #nette-debug nette-DbConnectionPanel tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
  8144. <h1>Queries: ' . count($this->queries) . ($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') . '</h1>
  8145. <div class="nette-inner nette-DbConnectionPanel">
  8146. <table>
  8147. <tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Params</th><th>Rows</th></tr>' . $s . '
  8148. </table>
  8149. </div>';
  8150. }
  8151. }
  8152. }
  8153. namespace Nette\Database\Drivers {
  8154. use Nette;
  8155. class MsSqlDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
  8156. {
  8157. public $supports = array('meta' => TRUE);
  8158. private $connection;
  8159. function __construct(Nette\Database\Connection $connection, array $options)
  8160. {
  8161. $this->connection = $connection;
  8162. }
  8163. function delimite($name)
  8164. {
  8165. return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']';
  8166. }
  8167. function formatDateTime(\DateTime $value)
  8168. {
  8169. return $value->format("'Y-m-d H:i:s'");
  8170. }
  8171. function formatLike($value, $pos)
  8172. {
  8173. $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
  8174. return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
  8175. }
  8176. function applyLimit(&$sql, $limit, $offset)
  8177. {
  8178. if ($limit >= 0) {
  8179. $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
  8180. }
  8181. if ($offset) {
  8182. throw new Nette\NotImplementedException('Offset is not implemented.');
  8183. }
  8184. }
  8185. function normalizeRow($row, $statement)
  8186. {
  8187. return $row;
  8188. }
  8189. }
  8190. class MySqlDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
  8191. {
  8192. public $supports = array('meta' => TRUE);
  8193. private $connection;
  8194. function __construct(Nette\Database\Connection $connection, array $options)
  8195. {
  8196. $this->connection = $connection;
  8197. $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
  8198. if ($charset) {
  8199. $connection->exec("SET NAMES '$charset'");
  8200. }
  8201. if (isset($options['sqlmode'])) {
  8202. $connection->exec("SET sql_mode='$options[sqlmode]'");
  8203. }
  8204. $connection->exec("SET time_zone='" . date('P') . "'");
  8205. }
  8206. function delimite($name)
  8207. {
  8208. return '`' . str_replace('`', '``', $name) . '`';
  8209. }
  8210. function formatDateTime(\DateTime $value)
  8211. {
  8212. return $value->format("'Y-m-d H:i:s'");
  8213. }
  8214. function formatLike($value, $pos)
  8215. {
  8216. $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
  8217. return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
  8218. }
  8219. function applyLimit(&$sql, $limit, $offset)
  8220. {
  8221. if ($limit >= 0 || $offset > 0) {
  8222. $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
  8223. . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
  8224. }
  8225. }
  8226. function normalizeRow($row, $statement)
  8227. {
  8228. return $row;
  8229. }
  8230. }
  8231. class OciDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
  8232. {
  8233. public $supports = array('meta' => TRUE);
  8234. private $connection;
  8235. private $fmtDateTime;
  8236. function __construct(Nette\Database\Connection $connection, array $options)
  8237. {
  8238. $this->connection = $connection;
  8239. $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U';
  8240. }
  8241. function delimite($name)
  8242. {
  8243. return '"' . str_replace('"', '""', $name) . '"';
  8244. }
  8245. function formatDateTime(\DateTime $value)
  8246. {
  8247. return $value->format($this->fmtDateTime);
  8248. }
  8249. function formatLike($value, $pos)
  8250. {
  8251. throw new Nette\NotImplementedException;
  8252. }
  8253. function applyLimit(&$sql, $limit, $offset)
  8254. {
  8255. if ($offset > 0) {
  8256. $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
  8257. . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
  8258. . ') WHERE "__rnum" > '. (int) $offset;
  8259. } elseif ($limit >= 0) {
  8260. $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
  8261. }
  8262. }
  8263. function normalizeRow($row, $statement)
  8264. {
  8265. return $row;
  8266. }
  8267. }
  8268. class OdbcDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
  8269. {
  8270. public $supports = array('meta' => TRUE);
  8271. private $connection;
  8272. function __construct(Nette\Database\Connection $connection, array $options)
  8273. {
  8274. $this->connection = $connection;
  8275. }
  8276. function delimite($name)
  8277. {
  8278. return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']';
  8279. }
  8280. function formatDateTime(\DateTime $value)
  8281. {
  8282. return $value->format("#m/d/Y H:i:s#");
  8283. }
  8284. function formatLike($value, $pos)
  8285. {
  8286. $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
  8287. return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
  8288. }
  8289. function applyLimit(&$sql, $limit, $offset)
  8290. {
  8291. if ($limit >= 0) {
  8292. $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
  8293. }
  8294. if ($offset) {
  8295. throw new Nette\InvalidArgumentException('Offset is not implemented in driver odbc.');
  8296. }
  8297. }
  8298. function normalizeRow($row, $statement)
  8299. {
  8300. return $row;
  8301. }
  8302. }
  8303. class PgSqlDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
  8304. {
  8305. public $supports = array('meta' => TRUE);
  8306. private $connection;
  8307. function __construct(Nette\Database\Connection $connection, array $options)
  8308. {
  8309. $this->connection = $connection;
  8310. }
  8311. function delimite($name)
  8312. {
  8313. return '"' . str_replace('"', '""', $name) . '"';
  8314. }
  8315. function formatDateTime(\DateTime $value)
  8316. {
  8317. return $value->format("'Y-m-d H:i:s'");
  8318. }
  8319. function formatLike($value, $pos)
  8320. {
  8321. throw new Nette\NotImplementedException;
  8322. }
  8323. function applyLimit(&$sql, $limit, $offset)
  8324. {
  8325. if ($limit >= 0)
  8326. $sql .= ' LIMIT ' . (int) $limit;
  8327. if ($offset > 0)
  8328. $sql .= ' OFFSET ' . (int) $offset;
  8329. }
  8330. function normalizeRow($row, $statement)
  8331. {
  8332. return $row;
  8333. }
  8334. }
  8335. class SqliteDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
  8336. {
  8337. public $supports = array('meta' => FALSE);
  8338. private $connection;
  8339. private $fmtDateTime;
  8340. function __construct(Nette\Database\Connection $connection, array $options)
  8341. {
  8342. $this->connection = $connection;
  8343. $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U';
  8344. }
  8345. function delimite($name)
  8346. {
  8347. return '[' . strtr($name, '[]', ' ') . ']';
  8348. }
  8349. function formatDateTime(\DateTime $value)
  8350. {
  8351. return $value->format($this->fmtDateTime);
  8352. }
  8353. function formatLike($value, $pos)
  8354. {
  8355. $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\');
  8356. return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
  8357. }
  8358. function applyLimit(&$sql, $limit, $offset)
  8359. {
  8360. if ($limit >= 0 || $offset > 0) {
  8361. $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
  8362. }
  8363. }
  8364. function normalizeRow($row, $statement)
  8365. {
  8366. return $row;
  8367. }
  8368. }
  8369. class Sqlite2Driver extends SqliteDriver
  8370. {
  8371. function formatLike($value, $pos)
  8372. {
  8373. throw new Nette\NotSupportedException;
  8374. }
  8375. function normalizeRow($row, $statement)
  8376. {
  8377. if (!is_object($row)) {
  8378. $iterator = $row;
  8379. } elseif ($row instanceof \Traversable) {
  8380. $iterator = iterator_to_array($row);
  8381. } else {
  8382. $iterator = (array) $row;
  8383. }
  8384. foreach ($iterator as $key => $value) {
  8385. unset($row[$key]);
  8386. if ($key[0] === '[' || $key[0] === '"') {
  8387. $key = substr($key, 1, -1);
  8388. }
  8389. $row[$key] = $value;
  8390. }
  8391. return $row;
  8392. }
  8393. }
  8394. }
  8395. namespace Nette\Database\Reflection {
  8396. use Nette;
  8397. class DatabaseReflection extends Nette\Object
  8398. {
  8399. const FIELD_TEXT = 'string',
  8400. FIELD_BINARY = 'bin',
  8401. FIELD_BOOL = 'bool',
  8402. FIELD_INTEGER = 'int',
  8403. FIELD_FLOAT = 'float',
  8404. FIELD_DATETIME = 'datetime';
  8405. private $primary;
  8406. private $foreign;
  8407. private $table;
  8408. function __construct($primary = 'id', $foreign = '%s_id', $table = '%s')
  8409. {
  8410. $this->primary = $primary;
  8411. $this->foreign = $foreign;
  8412. $this->table = $table;
  8413. }
  8414. function getPrimary($table)
  8415. {
  8416. return sprintf($this->primary, $table);
  8417. }
  8418. function getReferencingColumn($name, $table)
  8419. {
  8420. return $this->getReferencedColumn($table, $name);
  8421. }
  8422. function getReferencedColumn($name, $table)
  8423. {
  8424. if ($this->table !== '%s' && preg_match('(^' . str_replace('%s', '(.*)', preg_quote($this->table)) . '$)', $name, $match)) {
  8425. $name = $match[1];
  8426. }
  8427. return sprintf($this->foreign, $name, $table);
  8428. }
  8429. function getReferencedTable($name, $table)
  8430. {
  8431. return sprintf($this->table, $name, $table);
  8432. }
  8433. static function detectType($type)
  8434. {
  8435. static $types, $patterns = array(
  8436. 'BYTEA|BLOB|BIN' => self::FIELD_BINARY,
  8437. 'TEXT|CHAR' => self::FIELD_TEXT,
  8438. 'YEAR|BYTE|COUNTER|SERIAL|INT|LONG' => self::FIELD_INTEGER,
  8439. 'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => self::FIELD_FLOAT,
  8440. 'TIME|DATE' => self::FIELD_DATETIME,
  8441. 'BOOL|BIT' => self::FIELD_BOOL,
  8442. );
  8443. if (!isset($types[$type])) {
  8444. $types[$type] = 'string';
  8445. foreach ($patterns as $s => $val) {
  8446. if (preg_match("#$s#i", $type)) {
  8447. return $types[$type] = $val;
  8448. }
  8449. }
  8450. }
  8451. return $types[$type];
  8452. }
  8453. }
  8454. }
  8455. namespace Nette\Database {
  8456. use Nette;
  8457. class Row extends Nette\ArrayHash
  8458. {
  8459. function __construct($statement)
  8460. {
  8461. $statement->normalizeRow($this);
  8462. }
  8463. function offsetGet($key)
  8464. {
  8465. if (is_int($key)) {
  8466. $arr = array_values((array) $this);
  8467. return $arr[$key];
  8468. }
  8469. return $this->$key;
  8470. }
  8471. }
  8472. class SqlLiteral
  8473. {
  8474. public $value = '';
  8475. function __construct($value)
  8476. {
  8477. $this->value = (string) $value;
  8478. }
  8479. }
  8480. class SqlPreprocessor extends Nette\Object
  8481. {
  8482. private $connection;
  8483. private $driver;
  8484. private $params;
  8485. private $remaining;
  8486. private $counter;
  8487. private $arrayMode;
  8488. function __construct(Connection $connection)
  8489. {
  8490. $this->connection = $connection;
  8491. $this->driver = $connection->getSupplementalDriver();
  8492. }
  8493. function process($sql, $params)
  8494. {
  8495. $this->params = $params;
  8496. $this->counter = 0;
  8497. $this->remaining = array();
  8498. $cmd = strtoupper(substr(ltrim($sql), 0, 6));
  8499. $this->arrayMode = $cmd === 'INSERT' || $cmd === 'REPLAC' ? 'values' : 'assoc';
  8500. $sql = Nette\Utils\Strings::replace($sql, '~\'.*?\'|".*?"|:[a-zA-Z0-9_]+:|\?~s', array($this, 'callback'));
  8501. while ($this->counter < count($params)) {
  8502. $sql .= ' ' . $this->formatValue($params[$this->counter++]);
  8503. }
  8504. return array($sql, $this->remaining);
  8505. }
  8506. function callback($m)
  8507. {
  8508. $m = $m[0];
  8509. if ($m[0] === "'" || $m[0] === '"') {
  8510. return $m;
  8511. } elseif ($m[0] === '?') {
  8512. return $this->formatValue($this->params[$this->counter++]);
  8513. } elseif ($m[0] === ':') {
  8514. $s = substr($m, 1, -1);
  8515. return isset($this->connection->substitutions[$s]) ? $this->connection->substitutions[$s] : $m;
  8516. }
  8517. }
  8518. private function formatValue($value)
  8519. {
  8520. if (is_string($value)) {
  8521. if (strlen($value) > 20) {
  8522. $this->remaining[] = $value;
  8523. return '?';
  8524. } else {
  8525. return $this->connection->quote($value);
  8526. }
  8527. } elseif (is_int($value)) {
  8528. return (string) $value;
  8529. } elseif (is_float($value)) {
  8530. return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
  8531. } elseif (is_bool($value)) {
  8532. $this->remaining[] = $value;
  8533. return '?';
  8534. } elseif ($value === NULL) {
  8535. return 'NULL';
  8536. } elseif (is_array($value) || $value instanceof \Traversable) {
  8537. $vx = $kx = array();
  8538. if (isset($value[0])) {
  8539. foreach ($value as $v) {
  8540. $vx[] = $this->formatValue($v);
  8541. }
  8542. return implode(', ', $vx);
  8543. } elseif ($this->arrayMode === 'values') {
  8544. $this->arrayMode = 'multi';
  8545. foreach ($value as $k => $v) {
  8546. $kx[] = $this->driver->delimite($k);
  8547. $vx[] = $this->formatValue($v);
  8548. }
  8549. return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
  8550. } elseif ($this->arrayMode === 'assoc') {
  8551. foreach ($value as $k => $v) {
  8552. $vx[] = $this->driver->delimite($k) . '=' . $this->formatValue($v);
  8553. }
  8554. return implode(', ', $vx);
  8555. } elseif ($this->arrayMode === 'multi') {
  8556. foreach ($value as $k => $v) {
  8557. $vx[] = $this->formatValue($v);
  8558. }
  8559. return ', (' . implode(', ', $vx) . ')';
  8560. }
  8561. } elseif ($value instanceof \DateTime) {
  8562. return $this->driver->formatDateTime($value);
  8563. } elseif ($value instanceof SqlLiteral) {
  8564. return $value->value;
  8565. } else {
  8566. $this->remaining[] = $value;
  8567. return '?';
  8568. }
  8569. }
  8570. }
  8571. use PDO;use Nette\ObjectMixin;
  8572. if (class_exists('PDO')){ class Statement extends \PDOStatement
  8573. {
  8574. private $connection;
  8575. public $time;
  8576. private $types;
  8577. protected function __construct(Connection $connection)
  8578. {
  8579. $this->connection = $connection;
  8580. $this->setFetchMode(PDO::FETCH_CLASS, 'Nette\Database\Row', array($this));
  8581. }
  8582. function getConnection()
  8583. {
  8584. return $this->connection;
  8585. }
  8586. function execute($params = array())
  8587. {
  8588. static $types = array('boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT,
  8589. 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL);
  8590. foreach ($params as $key => $value) {
  8591. $type = gettype($value);
  8592. $this->bindValue(is_int($key) ? $key + 1 : $key, $value, isset($types[$type]) ? $types[$type] : PDO::PARAM_STR);
  8593. }
  8594. $time = microtime(TRUE);
  8595. try {
  8596. parent::execute();
  8597. } catch (\PDOException $e) {
  8598. $e->queryString = $this->queryString;
  8599. throw $e;
  8600. }
  8601. $this->time = microtime(TRUE) - $time;
  8602. $this->connection->__call('onQuery', array($this, $params));
  8603. return $this;
  8604. }
  8605. function fetchPairs()
  8606. {
  8607. return $this->fetchAll(PDO::FETCH_KEY_PAIR);
  8608. }
  8609. function normalizeRow($row)
  8610. {
  8611. if ($this->types === NULL) {
  8612. $this->types = array();
  8613. if ($this->connection->getSupplementalDriver()->supports['meta']) {
  8614. foreach ($row as $key => $foo) {
  8615. $type = $this->getColumnMeta(count($this->types));
  8616. if (isset($type['native_type'])) {
  8617. $this->types[$key] = Reflection\DatabaseReflection::detectType($type['native_type']);
  8618. }
  8619. }
  8620. }
  8621. }
  8622. foreach ($this->types as $key => $type) {
  8623. $value = $row[$key];
  8624. if ($value === NULL || $value === FALSE || $type === Reflection\DatabaseReflection::FIELD_TEXT) {
  8625. } elseif ($type === Reflection\DatabaseReflection::FIELD_INTEGER) {
  8626. $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
  8627. } elseif ($type === Reflection\DatabaseReflection::FIELD_FLOAT) {
  8628. $row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value;
  8629. } elseif ($type === Reflection\DatabaseReflection::FIELD_BOOL) {
  8630. $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
  8631. }
  8632. }
  8633. return $this->connection->getSupplementalDriver()->normalizeRow($row, $this);
  8634. }
  8635. function dump()
  8636. {
  8637. echo "\n<table class=\"dump\">\n<caption>" . htmlSpecialChars($this->queryString) . "</caption>\n";
  8638. if (!$this->columnCount()) {
  8639. echo "\t<tr>\n\t\t<th>Affected rows:</th>\n\t\t<td>", $this->rowCount(), "</td>\n\t</tr>\n</table>\n";
  8640. return;
  8641. }
  8642. $i = 0;
  8643. foreach ($this as $row) {
  8644. if ($i === 0) {
  8645. echo "<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
  8646. foreach ($row as $col => $foo) {
  8647. echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n";
  8648. }
  8649. echo "\t</tr>\n</thead>\n<tbody>\n";
  8650. }
  8651. echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
  8652. foreach ($row as $col) {
  8653. echo "\t\t<td>", htmlSpecialChars($col), "</td>\n";
  8654. }
  8655. echo "\t</tr>\n";
  8656. $i++;
  8657. }
  8658. if ($i === 0) {
  8659. echo "\t<tr>\n\t\t<td><em>empty result set</em></td>\n\t</tr>\n</table>\n";
  8660. } else {
  8661. echo "</tbody>\n</table>\n";
  8662. }
  8663. }
  8664. static function getReflection()
  8665. {
  8666. return new Nette\Reflection\ClassType(get_called_class());
  8667. }
  8668. function __call($name, $args)
  8669. {
  8670. return ObjectMixin::call($this, $name, $args);
  8671. }
  8672. function &__get($name)
  8673. {
  8674. return ObjectMixin::get($this, $name);
  8675. }
  8676. function __set($name, $value)
  8677. {
  8678. return ObjectMixin::set($this, $name, $value);
  8679. }
  8680. function __isset($name)
  8681. {
  8682. return ObjectMixin::has($this, $name);
  8683. }
  8684. function __unset($name)
  8685. {
  8686. ObjectMixin::remove($this, $name);
  8687. }
  8688. }
  8689. }
  8690. }
  8691. namespace Nette\Database\Table {
  8692. use Nette;
  8693. class ActiveRow extends Nette\Object implements \IteratorAggregate, \ArrayAccess
  8694. {
  8695. protected $table;
  8696. protected $data;
  8697. private $modified = array();
  8698. function __construct(array $data, Selection $table)
  8699. {
  8700. $this->data = $data;
  8701. $this->table = $table;
  8702. }
  8703. function __toString()
  8704. {
  8705. return (string) $this[$this->table->primary];
  8706. }
  8707. function toArray()
  8708. {
  8709. $this->access(NULL);
  8710. return $this->data;
  8711. }
  8712. function ref($name)
  8713. {
  8714. $referenced = $this->table->getReferencedTable($name, $column);
  8715. if (isset($referenced[$this[$column]])) {
  8716. $res = $referenced[$this[$column]];
  8717. return $res;
  8718. }
  8719. }
  8720. function related($table)
  8721. {
  8722. $referencing = $this->table->getReferencingTable($table);
  8723. $referencing->active = $this[$this->table->primary];
  8724. return $referencing;
  8725. }
  8726. function update($data = NULL)
  8727. {
  8728. if ($data === NULL) {
  8729. $data = $this->modified;
  8730. }
  8731. return $this->table->connection->table($this->table->name)
  8732. ->where($this->table->primary, $this[$this->table->primary])
  8733. ->update($data);
  8734. }
  8735. function delete()
  8736. {
  8737. return $this->table->connection->table($this->table->name)
  8738. ->where($this->table->primary, $this[$this->table->primary])
  8739. ->delete();
  8740. }
  8741. function getIterator()
  8742. {
  8743. $this->access(NULL);
  8744. return new \ArrayIterator($this->data);
  8745. }
  8746. function offsetSet($key, $value)
  8747. {
  8748. $this->__set($key, $value);
  8749. }
  8750. function offsetGet($key)
  8751. {
  8752. return $this->__get($key);
  8753. }
  8754. function offsetExists($key)
  8755. {
  8756. return $this->__isset($key);
  8757. }
  8758. function offsetUnset($key)
  8759. {
  8760. $this->__unset($key);
  8761. }
  8762. function __set($key, $value)
  8763. {
  8764. $this->data[$key] = $value;
  8765. $this->modified[$key] = $value;
  8766. }
  8767. function &__get($key)
  8768. {
  8769. if (array_key_exists($key, $this->data)) {
  8770. $this->access($key);
  8771. return $this->data[$key];
  8772. }
  8773. $column = $this->table->connection->databaseReflection->getReferencedColumn($key, $this->table->name);
  8774. if (array_key_exists($column, $this->data)) {
  8775. $value = $this->data[$column];
  8776. $referenced = $this->table->getReferencedTable($key);
  8777. $ret = isset($referenced[$value]) ? $referenced[$value] : NULL;
  8778. return $ret;
  8779. }
  8780. $this->access($key);
  8781. if (array_key_exists($key, $this->data)) {
  8782. return $this->data[$key];
  8783. } else {
  8784. $this->access($key, TRUE);
  8785. $this->access($column);
  8786. if (array_key_exists($column, $this->data)) {
  8787. $value = $this->data[$column];
  8788. $referenced = $this->table->getReferencedTable($key);
  8789. $ret = isset($referenced[$value]) ? $referenced[$value] : NULL;
  8790. } else {
  8791. $this->access($column, TRUE);
  8792. trigger_error("Unknown column $key", E_USER_WARNING);
  8793. $ret = NULL;
  8794. }
  8795. return $ret;
  8796. }
  8797. }
  8798. function __isset($key)
  8799. {
  8800. $this->access($key);
  8801. $return = array_key_exists($key, $this->data);
  8802. if (!$return) {
  8803. $this->access($key, TRUE);
  8804. }
  8805. return $return;
  8806. }
  8807. function __unset($key)
  8808. {
  8809. unset($this->data[$key]);
  8810. unset($this->modified[$key]);
  8811. }
  8812. function access($key, $delete = FALSE)
  8813. {
  8814. if ($this->table->connection->cache && $this->table->access($key, $delete)) {
  8815. $this->data = $this->table[$this->data[$this->table->primary]]->data;
  8816. }
  8817. }
  8818. }
  8819. use PDO;
  8820. class Selection extends Nette\Object implements \Iterator, \ArrayAccess, \Countable
  8821. {
  8822. public $connection;
  8823. public $name;
  8824. public $primary;
  8825. protected $rows;
  8826. protected $data;
  8827. protected $select = array();
  8828. protected $where = array();
  8829. protected $conditions = array();
  8830. protected $parameters = array();
  8831. protected $order = array();
  8832. protected $limit = NULL;
  8833. protected $offset = NULL;
  8834. protected $group = '';
  8835. protected $having = '';
  8836. protected $referenced = array();
  8837. protected $referencing = array();
  8838. protected $aggregation = array();
  8839. protected $accessed;
  8840. protected $prevAccessed;
  8841. protected $keys = array();
  8842. protected $delimitedName;
  8843. protected $delimitedPrimary;
  8844. function __construct($table, Nette\Database\Connection $connection)
  8845. {
  8846. $this->name = $table;
  8847. $this->connection = $connection;
  8848. $this->primary = $this->getPrimary($table);
  8849. $this->delimitedName = $connection->getSupplementalDriver()->delimite($this->name);
  8850. $this->delimitedPrimary = $connection->getSupplementalDriver()->delimite($this->primary);
  8851. }
  8852. function __destruct()
  8853. {
  8854. if ($this->connection->cache && !$this->select && $this->rows !== NULL) {
  8855. $accessed = $this->accessed;
  8856. if (is_array($accessed)) {
  8857. $accessed = array_filter($accessed);
  8858. }
  8859. $this->connection->cache->save(array(__CLASS__, $this->name, $this->conditions), $accessed);
  8860. }
  8861. $this->rows = NULL;
  8862. }
  8863. function get($key)
  8864. {
  8865. $clone = clone $this;
  8866. $clone->where($this->delimitedPrimary, $key);
  8867. return $clone->fetch();
  8868. }
  8869. function select($columns)
  8870. {
  8871. $this->__destruct();
  8872. $this->select[] = $this->tryDelimite($columns);
  8873. return $this;
  8874. }
  8875. function where($condition, $parameters = array())
  8876. {
  8877. if (is_array($condition)) {
  8878. foreach ($condition as $key => $val) {
  8879. $this->where($key, $val);
  8880. }
  8881. return $this;
  8882. }
  8883. $this->__destruct();
  8884. $this->conditions[] = $condition;
  8885. $condition = $this->tryDelimite($condition);
  8886. $args = func_num_args();
  8887. if ($args !== 2 || strpbrk($condition, '?:')) {
  8888. if ($args !== 2 || !is_array($parameters)) {
  8889. $parameters = func_get_args();
  8890. array_shift($parameters);
  8891. }
  8892. $this->parameters = array_merge($this->parameters, $parameters);
  8893. } elseif ($parameters === NULL) {
  8894. $condition .= ' IS NULL';
  8895. } elseif ($parameters instanceof Selection) {
  8896. $clone = clone $parameters;
  8897. if (!$clone->select) {
  8898. $clone->select = array($this->getPrimary($clone->name));
  8899. }
  8900. if ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql') {
  8901. $condition .= " IN ($clone)";
  8902. } else {
  8903. $in = array();
  8904. foreach ($clone as $row) {
  8905. $this->parameters[] = array_values(iterator_to_array($row));
  8906. $in[] = (count($row) === 1 ? '?' : '(?)');
  8907. }
  8908. $condition .= ' IN (' . ($in ? implode(', ', $in) : 'NULL') . ')';
  8909. }
  8910. } elseif (!is_array($parameters)) {
  8911. $condition .= ' = ?';
  8912. $this->parameters[] = $parameters;
  8913. } else {
  8914. if ($parameters) {
  8915. $condition .= " IN (?)";
  8916. $this->parameters[] = $parameters;
  8917. } else {
  8918. $condition .= " IN (NULL)";
  8919. }
  8920. }
  8921. $this->where[] = $condition;
  8922. return $this;
  8923. }
  8924. function order($columns)
  8925. {
  8926. $this->rows = NULL;
  8927. $this->order[] = $this->tryDelimite($columns);
  8928. return $this;
  8929. }
  8930. function limit($limit, $offset = NULL)
  8931. {
  8932. $this->rows = NULL;
  8933. $this->limit = $limit;
  8934. $this->offset = $offset;
  8935. return $this;
  8936. }
  8937. function page($page, $itemsPerPage)
  8938. {
  8939. $this->rows = NULL;
  8940. $this->limit = $itemsPerPage;
  8941. $this->offset = ($page - 1) * $itemsPerPage;
  8942. return $this;
  8943. }
  8944. function group($columns, $having = '')
  8945. {
  8946. $this->__destruct();
  8947. $this->group = $columns;
  8948. $this->having = $having;
  8949. return $this;
  8950. }
  8951. function aggregation($function)
  8952. {
  8953. $join = $this->createJoins(implode(',', $this->conditions), TRUE) + $this->createJoins($function);
  8954. $query = "SELECT $function FROM $this->delimitedName" . implode($join);
  8955. if ($this->where) {
  8956. $query .= ' WHERE (' . implode(') AND (', $this->where) . ')';
  8957. }
  8958. foreach ($this->query($query)->fetch() as $val) {
  8959. return $val;
  8960. }
  8961. }
  8962. function count($column = '')
  8963. {
  8964. if (!$column) {
  8965. $this->execute();
  8966. return count($this->data);
  8967. }
  8968. return $this->aggregation("COUNT({$this->tryDelimite($column)})");
  8969. }
  8970. function min($column)
  8971. {
  8972. return $this->aggregation("MIN({$this->tryDelimite($column)})");
  8973. }
  8974. function max($column)
  8975. {
  8976. return $this->aggregation("MAX({$this->tryDelimite($column)})");
  8977. }
  8978. function sum($column)
  8979. {
  8980. return $this->aggregation("SUM({$this->tryDelimite($column)})");
  8981. }
  8982. function getSql()
  8983. {
  8984. $join = $this->createJoins(implode(',', $this->conditions), TRUE)
  8985. + $this->createJoins(implode(',', $this->select) . ",$this->group,$this->having," . implode(',', $this->order));
  8986. if ($this->rows === NULL && $this->connection->cache && !is_string($this->prevAccessed)) {
  8987. $this->accessed = $this->prevAccessed = $this->connection->cache->load(array(__CLASS__, $this->name, $this->conditions));
  8988. }
  8989. $prefix = $join ? "$this->delimitedName." : '';
  8990. if ($this->select) {
  8991. $cols = implode(', ', $this->select);
  8992. } elseif ($this->prevAccessed) {
  8993. $cols = $prefix . implode(', ' . $prefix, array_map(array($this->connection->getSupplementalDriver(), 'delimite'), array_keys($this->prevAccessed)));
  8994. } else {
  8995. $cols = $prefix . '*';
  8996. }
  8997. return "SELECT{$this->topString()} $cols FROM $this->delimitedName" . implode($join) . $this->whereString();
  8998. }
  8999. protected function createJoins($val, $inner = FALSE)
  9000. {
  9001. $supplementalDriver = $this->connection->getSupplementalDriver();
  9002. $joins = array();
  9003. preg_match_all('~\\b([a-z][\\w.]*)\\.([a-z]\\w*)(\\s+IS\\b|\\s*<=>)?~i', $val, $matches, PREG_SET_ORDER);
  9004. foreach ($matches as $match) {
  9005. if ($match[1] !== $this->name) {
  9006. foreach (explode('.', $match[1]) as $name) {
  9007. $table = $this->connection->databaseReflection->getReferencedTable($name, $this->name);
  9008. $column = $this->connection->databaseReflection->getReferencedColumn($name, $this->name);
  9009. $primary = $this->getPrimary($table);
  9010. $joins[$name] = ' ' . (!isset($joins[$name]) && $inner && !isset($match[3]) ? 'INNER' : 'LEFT')
  9011. . ' JOIN ' . $supplementalDriver->delimite($table)
  9012. . ' AS '. $supplementalDriver->delimite($table)
  9013. . ($table !== $name ? ' AS ' . $supplementalDriver->delimite($name) : '')
  9014. . " ON $this->delimitedName." . $supplementalDriver->delimite($column)
  9015. . ' = ' . $supplementalDriver->delimite($name) . '.' . $supplementalDriver->delimite($primary);
  9016. }
  9017. }
  9018. }
  9019. return $joins;
  9020. }
  9021. protected function execute()
  9022. {
  9023. if ($this->rows !== NULL) {
  9024. return;
  9025. }
  9026. try {
  9027. $result = $this->query($this->getSql());
  9028. } catch (\PDOException $exception) {
  9029. if (!$this->select && $this->prevAccessed) {
  9030. $this->prevAccessed = '';
  9031. $this->accessed = array();
  9032. $result = $this->query($this->getSql());
  9033. } else {
  9034. throw $exception;
  9035. }
  9036. }
  9037. $this->rows = array();
  9038. $result->setFetchMode(PDO::FETCH_ASSOC);
  9039. foreach ($result as $key => $row) {
  9040. $row = $result->normalizeRow($row);
  9041. $this->rows[isset($row[$this->primary]) ? $row[$this->primary] : $key] = new ActiveRow($row, $this);
  9042. }
  9043. $this->data = $this->rows;
  9044. if (isset($row[$this->primary]) && !is_string($this->accessed)) {
  9045. $this->accessed[$this->primary] = TRUE;
  9046. }
  9047. }
  9048. protected function whereString()
  9049. {
  9050. $return = '';
  9051. $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
  9052. $where = $this->where;
  9053. if ($this->limit !== NULL && $driver === 'oci') {
  9054. $where[] = ($this->offset ? "rownum > $this->offset AND " : '') . 'rownum <= ' . ($this->limit + $this->offset);
  9055. }
  9056. if ($where) {
  9057. $return .= ' WHERE (' . implode(') AND (', $where) . ')';
  9058. }
  9059. if ($this->group) {
  9060. $return .= ' GROUP BY '. $this->tryDelimite($this->group);
  9061. }
  9062. if ($this->having) {
  9063. $return .= ' HAVING '. $this->tryDelimite($this->having);
  9064. }
  9065. if ($this->order) {
  9066. $return .= ' ORDER BY ' . implode(', ', $this->order);
  9067. }
  9068. if ($this->limit !== NULL && $driver !== 'oci' && $driver !== 'dblib') {
  9069. $return .= " LIMIT $this->limit";
  9070. if ($this->offset !== NULL) {
  9071. $return .= " OFFSET $this->offset";
  9072. }
  9073. }
  9074. return $return;
  9075. }
  9076. protected function topString()
  9077. {
  9078. if ($this->limit !== NULL && $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) === 'dblib') {
  9079. return " TOP ($this->limit)";
  9080. }
  9081. return '';
  9082. }
  9083. protected function tryDelimite($s)
  9084. {
  9085. return preg_match('#^[a-z_][a-z0-9_.]*$#i', $s)
  9086. ? implode('.', array_map(array($this->connection->getSupplementalDriver(), 'delimite'), explode('.', $s)))
  9087. : $s;
  9088. }
  9089. protected function query($query)
  9090. {
  9091. return $this->connection->queryArgs($query, $this->parameters);
  9092. }
  9093. function access($key, $delete = FALSE)
  9094. {
  9095. if ($delete) {
  9096. if (is_array($this->accessed)) {
  9097. $this->accessed[$key] = FALSE;
  9098. }
  9099. return FALSE;
  9100. }
  9101. if ($key === NULL) {
  9102. $this->accessed = '';
  9103. } elseif (!is_string($this->accessed)) {
  9104. $this->accessed[$key] = TRUE;
  9105. }
  9106. if (!$this->select && $this->prevAccessed && ($key === NULL || !isset($this->prevAccessed[$key]))) {
  9107. $this->prevAccessed = '';
  9108. $this->rows = NULL;
  9109. return TRUE;
  9110. }
  9111. return FALSE;
  9112. }
  9113. function insert($data)
  9114. {
  9115. if ($data instanceof Selection) {
  9116. $data = $data->getSql();
  9117. } elseif ($data instanceof \Traversable) {
  9118. $data = iterator_to_array($data);
  9119. }
  9120. $return = $this->connection->query("INSERT INTO $this->delimitedName", $data);
  9121. $this->rows = NULL;
  9122. if (!is_array($data)) {
  9123. return $return->rowCount();
  9124. }
  9125. if (!isset($data[$this->primary]) && ($id = $this->connection->lastInsertId())) {
  9126. $data[$this->primary] = $id;
  9127. }
  9128. return new ActiveRow($data, $this);
  9129. }
  9130. function update($data)
  9131. {
  9132. if ($data instanceof \Traversable) {
  9133. $data = iterator_to_array($data);
  9134. } elseif (!is_array($data)) {
  9135. throw new Nette\InvalidArgumentException;
  9136. }
  9137. if (!$data) {
  9138. return 0;
  9139. }
  9140. return $this->connection->queryArgs(
  9141. 'UPDATE' . $this->topString() . " $this->delimitedName SET ?" . $this->whereString(),
  9142. array_merge(array($data), $this->parameters)
  9143. )->rowCount();
  9144. }
  9145. function delete()
  9146. {
  9147. return $this->query(
  9148. 'DELETE' . $this->topString() . " FROM $this->delimitedName" . $this->whereString()
  9149. )->rowCount();
  9150. }
  9151. function getReferencedTable($name, & $column = NULL)
  9152. {
  9153. $column = $this->connection->databaseReflection->getReferencedColumn($name, $this->name);
  9154. $referenced = & $this->referenced[$name];
  9155. if ($referenced === NULL) {
  9156. $keys = array();
  9157. foreach ($this->rows as $row) {
  9158. if ($row[$column] !== NULL) {
  9159. $keys[$row[$column]] = NULL;
  9160. }
  9161. }
  9162. if ($keys) {
  9163. $table = $this->connection->databaseReflection->getReferencedTable($name, $this->name);
  9164. $referenced = new Selection($table, $this->connection);
  9165. $referenced->where($table . '.' . $this->getPrimary($table), array_keys($keys));
  9166. } else {
  9167. $referenced = array();
  9168. }
  9169. }
  9170. return $referenced;
  9171. }
  9172. function getReferencingTable($table)
  9173. {
  9174. $column = $this->connection->databaseReflection->getReferencingColumn($table, $this->name);
  9175. $referencing = new GroupedSelection($table, $this, $column);
  9176. $referencing->where("$table.$column", array_keys((array) $this->rows));
  9177. return $referencing;
  9178. }
  9179. private function getPrimary($table)
  9180. {
  9181. return $this->connection->databaseReflection->getPrimary($table);
  9182. }
  9183. function rewind()
  9184. {
  9185. $this->execute();
  9186. $this->keys = array_keys($this->data);
  9187. reset($this->keys);
  9188. }
  9189. function current()
  9190. {
  9191. return $this->data[current($this->keys)];
  9192. }
  9193. function key()
  9194. {
  9195. return current($this->keys);
  9196. }
  9197. function next()
  9198. {
  9199. next($this->keys);
  9200. }
  9201. function valid()
  9202. {
  9203. return current($this->keys) !== FALSE;
  9204. }
  9205. function offsetSet($key, $value)
  9206. {
  9207. $this->execute();
  9208. $this->data[$key] = $value;
  9209. }
  9210. function offsetGet($key)
  9211. {
  9212. $this->execute();
  9213. return $this->data[$key];
  9214. }
  9215. function offsetExists($key)
  9216. {
  9217. $this->execute();
  9218. return isset($this->data[$key]);
  9219. }
  9220. function offsetUnset($key)
  9221. {
  9222. $this->execute();
  9223. unset($this->data[$key]);
  9224. }
  9225. function fetch()
  9226. {
  9227. $this->execute();
  9228. $return = current($this->data);
  9229. next($this->data);
  9230. return $return;
  9231. }
  9232. function fetchPairs($key, $value = '')
  9233. {
  9234. $return = array();
  9235. foreach ($this as $row) {
  9236. $return[$row[$key]] = ($value !== '' ? $row[$value] : $row);
  9237. }
  9238. return $return;
  9239. }
  9240. }
  9241. class GroupedSelection extends Selection
  9242. {
  9243. private $refTable;
  9244. private $column;
  9245. private $delimitedColumn;
  9246. public $active;
  9247. function __construct($name, Selection $refTable, $column)
  9248. {
  9249. parent::__construct($name, $refTable->connection);
  9250. $this->refTable = $refTable;
  9251. $this->through($column);
  9252. }
  9253. function through($column)
  9254. {
  9255. $this->column = $column;
  9256. $this->delimitedColumn = $this->refTable->connection->getSupplementalDriver()->delimite($this->column);
  9257. return $this;
  9258. }
  9259. function select($columns)
  9260. {
  9261. if (!$this->select) {
  9262. $this->select[] = "$this->delimitedName.$this->delimitedColumn";
  9263. }
  9264. return parent::select($columns);
  9265. }
  9266. function order($columns)
  9267. {
  9268. if (!$this->order) {
  9269. $this->order[] = "$this->delimitedName.$this->delimitedColumn"
  9270. . (preg_match('~\\bDESC$~i', $columns) ? ' DESC' : '');
  9271. }
  9272. return parent::order($columns);
  9273. }
  9274. function aggregation($function)
  9275. {
  9276. $join = $this->createJoins(implode(',', $this->conditions), TRUE) + $this->createJoins($function);
  9277. $column = ($join ? "$this->delimitedName." : '') . $this->delimitedColumn;
  9278. $query = "SELECT $function, $column FROM $this->delimitedName" . implode($join);
  9279. if ($this->where) {
  9280. $query .= ' WHERE (' . implode(') AND (', $this->where) . ')';
  9281. }
  9282. $query .= " GROUP BY $column";
  9283. $aggregation = & $this->refTable->aggregation[$query];
  9284. if ($aggregation === NULL) {
  9285. $aggregation = array();
  9286. foreach ($this->query($query, $this->parameters) as $row) {
  9287. $aggregation[$row[$this->column]] = $row;
  9288. }
  9289. }
  9290. if (isset($aggregation[$this->active])) {
  9291. foreach ($aggregation[$this->active] as $val) {
  9292. return $val;
  9293. }
  9294. }
  9295. }
  9296. function count($column = '')
  9297. {
  9298. $return = parent::count($column);
  9299. return isset($return) ? $return : 0;
  9300. }
  9301. function insert($data)
  9302. {
  9303. if ($data instanceof \Traversable && !$data instanceof Selection) {
  9304. $data = iterator_to_array($data);
  9305. }
  9306. if (is_array($data)) {
  9307. $data[$this->column] = $this->active;
  9308. }
  9309. return parent::insert($data);
  9310. }
  9311. function update($data)
  9312. {
  9313. $where = $this->where;
  9314. $this->where[0] = "$this->delimitedColumn = " . $this->connection->quote($this->active);
  9315. $return = parent::update($data);
  9316. $this->where = $where;
  9317. return $return;
  9318. }
  9319. function delete()
  9320. {
  9321. $where = $this->where;
  9322. $this->where[0] = "$this->delimitedColumn = " . $this->connection->quote($this->active);
  9323. $return = parent::delete();
  9324. $this->where = $where;
  9325. return $return;
  9326. }
  9327. protected function execute()
  9328. {
  9329. if ($this->rows !== NULL) {
  9330. return;
  9331. }
  9332. $referencing = & $this->refTable->referencing[$this->getSql()];
  9333. if ($referencing === NULL) {
  9334. $limit = $this->limit;
  9335. $rows = count($this->refTable->rows);
  9336. if ($this->limit && $rows > 1) {
  9337. $this->limit = NULL;
  9338. }
  9339. parent::execute();
  9340. $this->limit = $limit;
  9341. $referencing = array();
  9342. $offset = array();
  9343. foreach ($this->rows as $key => $row) {
  9344. $ref = & $referencing[$row[$this->column]];
  9345. $skip = & $offset[$row[$this->column]];
  9346. if ($limit === NULL || $rows <= 1 || (count($ref) < $limit && $skip >= $this->offset)) {
  9347. $ref[$key] = $row;
  9348. } else {
  9349. unset($this->rows[$key]);
  9350. }
  9351. $skip++;
  9352. unset($ref, $skip);
  9353. }
  9354. }
  9355. $this->data = & $referencing[$this->active];
  9356. if ($this->data === NULL) {
  9357. $this->data = array();
  9358. }
  9359. }
  9360. }
  9361. }
  9362. namespace Nette\DI {
  9363. use Nette;
  9364. class Container extends Nette\FreezableObject implements IContainer
  9365. {
  9366. const TAG_TYPEHINT = 'typeHint';
  9367. public $params = array();
  9368. private $registry = array();
  9369. private $factories = array();
  9370. private $tags = array();
  9371. private $creating;
  9372. function addService($name, $service, $tags = NULL)
  9373. {
  9374. $this->updating();
  9375. if (!is_string($name) || $name === '') {
  9376. throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
  9377. }
  9378. if (isset($this->registry[$name]) || method_exists($this, "createService$name")) {
  9379. throw new Nette\InvalidStateException("Service '$name' has already been registered.");
  9380. }
  9381. if (is_string($tags)) {
  9382. $tags = array(self::TAG_TYPEHINT => array($tags));
  9383. } elseif (is_array($tags)) {
  9384. foreach ($tags as $id => $attrs) {
  9385. if (is_int($id) && is_string($attrs)) {
  9386. $tags[$attrs] = array();
  9387. unset($tags[$id]);
  9388. } elseif (!is_array($attrs)) {
  9389. $tags[$id] = (array) $attrs;
  9390. }
  9391. }
  9392. }
  9393. if (is_string($service) && strpos($service, ':') === FALSE) {
  9394. if (!isset($tags[self::TAG_TYPEHINT][0])) {
  9395. $tags[self::TAG_TYPEHINT][0] = $service;
  9396. }
  9397. $service = new ServiceBuilder($service);
  9398. }
  9399. if ($service instanceof IServiceBuilder) {
  9400. $factory = array($service, 'createService');
  9401. } elseif (is_object($service) && !$service instanceof \Closure && !$service instanceof Nette\Callback) {
  9402. $this->registry[$name] = $service;
  9403. $this->tags[$name] = $tags;
  9404. return $this;
  9405. } else {
  9406. $factory = $service;
  9407. }
  9408. $this->factories[$name] = array(callback($factory));
  9409. $this->tags[$name] = $tags;
  9410. $this->registry[$name] = & $this->factories[$name][1];
  9411. return $service;
  9412. }
  9413. function removeService($name)
  9414. {
  9415. $this->updating();
  9416. unset($this->registry[$name], $this->factories[$name]);
  9417. }
  9418. function getService($name)
  9419. {
  9420. if (isset($this->registry[$name])) {
  9421. return $this->registry[$name];
  9422. } elseif (isset($this->creating[$name])) {
  9423. throw new Nette\InvalidStateException("Circular reference detected for services: "
  9424. . implode(', ', array_keys($this->creating)) . ".");
  9425. }
  9426. if (isset($this->factories[$name])) {
  9427. list($factory) = $this->factories[$name];
  9428. if (!$factory->isCallable()) {
  9429. throw new Nette\InvalidStateException("Unable to create service '$name', factory '$factory' is not callable.");
  9430. }
  9431. $this->creating[$name] = TRUE;
  9432. try {
  9433. $service = $factory($this);
  9434. } catch (\Exception $e) {}
  9435. } elseif (method_exists($this, $factory = 'createService' . ucfirst($name))) {
  9436. $this->creating[$name] = TRUE;
  9437. try {
  9438. $service = $this->$factory();
  9439. } catch (\Exception $e) {}
  9440. } else {
  9441. throw new MissingServiceException("Service '$name' not found.");
  9442. }
  9443. unset($this->creating[$name]);
  9444. if (isset($e)) {
  9445. throw $e;
  9446. } elseif (!is_object($service)) {
  9447. throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not object.");
  9448. } elseif (isset($this->tags[$name][self::TAG_TYPEHINT][0]) && !$service instanceof $this->tags[$name][self::TAG_TYPEHINT][0]) {
  9449. throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not '{$this->tags[$name][self::TAG_TYPEHINT][0]}' type.");
  9450. }
  9451. unset($this->factories[$name]);
  9452. return $this->registry[$name] = $service;
  9453. }
  9454. function getServiceByType($type)
  9455. {
  9456. foreach ($this->registry as $name => $service) {
  9457. if (isset($this->tags[$name][self::TAG_TYPEHINT][0]) ? !strcasecmp($this->tags[$name][self::TAG_TYPEHINT][0], $type) : $service instanceof $type) {
  9458. $found[] = $name;
  9459. }
  9460. }
  9461. if (!isset($found)) {
  9462. throw new MissingServiceException("Service matching '$type' type not found.");
  9463. } elseif (count($found) > 1) {
  9464. throw new AmbiguousServiceException("Found more than one service ('" . implode("', '", $found) . "') matching '$type' type.");
  9465. }
  9466. return $this->getService($found[0]);
  9467. }
  9468. function getServiceNamesByTag($tag)
  9469. {
  9470. $found = array();
  9471. foreach ($this->registry as $name => $service) {
  9472. if (isset($this->tags[$name][$tag])) {
  9473. $found[$name] = $this->tags[$name][$tag];
  9474. }
  9475. }
  9476. return $found;
  9477. }
  9478. function hasService($name)
  9479. {
  9480. return isset($this->registry[$name])
  9481. || isset($this->factories[$name])
  9482. || method_exists($this, "createService$name");
  9483. }
  9484. function checkServiceType($name, $type)
  9485. {
  9486. return isset($this->tags[$name][self::TAG_TYPEHINT][0])
  9487. ? !strcasecmp($this->tags[$name][self::TAG_TYPEHINT][0], $type)
  9488. : (isset($this->registry[$name]) && $this->registry[$name] instanceof $type);
  9489. }
  9490. function expand($s)
  9491. {
  9492. if (is_string($s) && strpos($s, '%') !== FALSE) {
  9493. $that = $this;
  9494. return @preg_replace_callback('#%([a-z0-9._-]*)%#i', function ($m) use($that) {
  9495. list(, $param) = $m;
  9496. if ($param === '') {
  9497. return '%';
  9498. } elseif (!is_scalar($val = Nette\Utils\Arrays::get((array) $that->params, explode('.', $param)))) {
  9499. throw new Nette\InvalidStateException("Parameter '$param' is not scalar.");
  9500. }
  9501. return $val;
  9502. }, $s);
  9503. }
  9504. return $s;
  9505. }
  9506. function &__get($name)
  9507. {
  9508. if (!isset($this->registry[$name])) {
  9509. $this->getService($name);
  9510. }
  9511. return $this->registry[$name];
  9512. }
  9513. function __set($name, $service)
  9514. {
  9515. $this->updating();
  9516. if (!is_string($name) || $name === '') {
  9517. throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
  9518. } elseif (isset($this->registry[$name]) || method_exists($this, "createService$name")) {
  9519. throw new Nette\InvalidStateException("Service '$name' has already been registered.");
  9520. } elseif (!is_object($service)) {
  9521. throw new Nette\InvalidArgumentException("Service must be a object, " . gettype($service) . " given.");
  9522. }
  9523. $this->registry[$name] = $service;
  9524. }
  9525. function __isset($name)
  9526. {
  9527. return $this->hasService($name);
  9528. }
  9529. function __unset($name)
  9530. {
  9531. $this->removeService($name);
  9532. }
  9533. }
  9534. class ContainerBuilder extends Nette\Object
  9535. {
  9536. function addDefinitions(IContainer $container, array $definitions)
  9537. {
  9538. foreach ($definitions as $name => $definition) {
  9539. if (!is_array($definition)) {
  9540. $definition = array('class' => $definition);
  9541. }
  9542. $arguments = isset($definition['arguments']) ? $definition['arguments'] : array();
  9543. $expander = function(&$val) use($container) {
  9544. $val = $val[0] === '@' ? $container->getService(substr($val, 1)) : $container->expand($val);
  9545. };
  9546. if (isset($definition['class'])) {
  9547. $class = $definition['class'];
  9548. $methods = isset($definition['methods']) ? $definition['methods'] : array();
  9549. $factory = function($container) use($class, $arguments, $methods, $expander) {
  9550. $class = $container->expand($class);
  9551. if ($arguments) {
  9552. array_walk_recursive($arguments, $expander);
  9553. $service = Nette\Reflection\ClassType::from($class)->newInstanceArgs($arguments);
  9554. } else {
  9555. $service = new $class;
  9556. }
  9557. array_walk_recursive($methods, $expander);
  9558. foreach ($methods as $method) {
  9559. call_user_func_array(array($service, $method[0]), isset($method[1]) ? $method[1] : array());
  9560. }
  9561. return $service;
  9562. };
  9563. } elseif (isset($definition['factory'])) {
  9564. array_unshift($arguments, $definition['factory']);
  9565. $factory = function($container) use($arguments, $expander) {
  9566. array_walk_recursive($arguments, $expander);
  9567. $factory = $arguments[0]; $arguments[0] = $container;
  9568. return call_user_func_array($factory, $arguments);
  9569. };
  9570. } else {
  9571. throw new Nette\InvalidStateException("The definition of service '$name' is missing factory method.");
  9572. }
  9573. if (isset($definition['tags'])) {
  9574. $tags = (array) $definition['tags'];
  9575. array_walk_recursive($tags, $expander);
  9576. } else {
  9577. $tags = NULL;
  9578. }
  9579. $container->addService($name, $factory, $tags);
  9580. }
  9581. }
  9582. function generateCode(array $definitions)
  9583. {
  9584. $code = '';
  9585. foreach ($definitions as $name => $definition) {
  9586. $name = $this->varExport($name);
  9587. if (is_scalar($definition)) {
  9588. $factory = $this->varExport($definition);
  9589. $code .= "\$container->addService($name, $factory);\n\n";
  9590. continue;
  9591. }
  9592. $arguments = $this->argsExport(isset($definition['arguments']) ? $definition['arguments'] : array());
  9593. if (isset($definition['class'])) {
  9594. $class = $this->argsExport(array($definition['class']));
  9595. $methods = isset($definition['methods']) ? $definition['methods'] : array();
  9596. $factory = "function(\$container) {\n\t\$class = $class; \$service = new \$class($arguments);\n";
  9597. foreach ($methods as $method) {
  9598. $args = isset($method[1]) ? $this->argsExport($method[1]) : '';
  9599. $factory .= "\t\$service->$method[0]($args);\n";
  9600. }
  9601. $factory .= "\treturn \$service;\n}";
  9602. } elseif (isset($definition['factory'])) {
  9603. $factory = $this->argsExport(array($definition['factory']));
  9604. $factory = "function(\$container) {\n\treturn call_user_func(\n\t\t$factory,\n\t\t\$container"
  9605. . ($arguments ? ",\n\t\t$arguments" : '') . "\n\t);\n}";
  9606. } else {
  9607. throw new Nette\InvalidStateException("The definition of service '$name' is missing factory method.");
  9608. }
  9609. $tags = isset($definition['tags']) ? $this->argsExport(array($definition['tags'])) : 'NULL';
  9610. $code .= "\$container->addService($name, $factory, $tags);\n\n";
  9611. }
  9612. return $code;
  9613. }
  9614. private function argsExport($args)
  9615. {
  9616. $args = implode(', ', array_map(array($this, 'varExport'), $args));
  9617. $args = preg_replace("#'@(\w+)'#", '\$container->getService(\'$1\')', $args);
  9618. $args = preg_replace("#('[^']*%[^']*')#", '\$container->expand($1)', $args);
  9619. return $args;
  9620. }
  9621. private function varExport($arg)
  9622. {
  9623. return preg_replace('#\n *#', ' ', var_export($arg, TRUE));
  9624. }
  9625. }
  9626. class MissingServiceException extends Nette\InvalidStateException
  9627. {
  9628. }
  9629. class AmbiguousServiceException extends Nette\InvalidStateException
  9630. {
  9631. }
  9632. class ServiceBuilder extends Nette\Object implements IServiceBuilder
  9633. {
  9634. private $class;
  9635. function __construct($class)
  9636. {
  9637. $this->class = $class;
  9638. }
  9639. function getClass()
  9640. {
  9641. return $this->class;
  9642. }
  9643. function createService(Nette\DI\IContainer $container)
  9644. {
  9645. if (!class_exists($this->class)) {
  9646. throw new Nette\InvalidStateException("Cannot instantiate service, class '$this->class' not found.");
  9647. }
  9648. return new $this->class;
  9649. }
  9650. }
  9651. }
  9652. namespace Nette\Diagnostics {
  9653. use Nette;
  9654. final class Helpers
  9655. {
  9656. static function editorLink($file, $line)
  9657. {
  9658. if (Debugger::$editor && is_file($file)) {
  9659. $dir = dirname(strtr($file, '/', DIRECTORY_SEPARATOR));
  9660. $base = isset($_SERVER['SCRIPT_FILENAME']) ? dirname(dirname(strtr($_SERVER['SCRIPT_FILENAME'], '/', DIRECTORY_SEPARATOR))) : dirname($dir);
  9661. if (substr($dir, 0, strlen($base)) === $base) {
  9662. $dir = '...' . substr($dir, strlen($base));
  9663. }
  9664. return Nette\Utils\Html::el('a')
  9665. ->href(strtr(Debugger::$editor, array('%file' => rawurlencode($file), '%line' => $line)))
  9666. ->title("$file:$line")
  9667. ->setHtml(htmlSpecialChars(rtrim($dir, DIRECTORY_SEPARATOR)) . DIRECTORY_SEPARATOR . '<b>' . htmlSpecialChars(basename($file)) . '</b>');
  9668. } else {
  9669. return Nette\Utils\Html::el('span')->setText($file);
  9670. }
  9671. }
  9672. static function htmlDump(&$var, $level = 0)
  9673. {
  9674. static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
  9675. if ($tableUtf === NULL) {
  9676. foreach (range("\x00", "\xFF") as $ch) {
  9677. if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) {
  9678. $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
  9679. } elseif (ord($ch) < 127) {
  9680. $tableUtf[$ch] = $tableBin[$ch] = $ch;
  9681. } else {
  9682. $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch));
  9683. }
  9684. }
  9685. $tableBin["\\"] = '\\\\';
  9686. $tableBin["\r"] = '\\r';
  9687. $tableBin["\n"] = '\\n';
  9688. $tableBin["\t"] = '\\t';
  9689. $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
  9690. }
  9691. if (is_bool($var)) {
  9692. return ($var ? 'TRUE' : 'FALSE') . "\n";
  9693. } elseif ($var === NULL) {
  9694. return "NULL\n";
  9695. } elseif (is_int($var)) {
  9696. return "$var\n";
  9697. } elseif (is_float($var)) {
  9698. $var = var_export($var, TRUE);
  9699. if (strpos($var, '.') === FALSE) {
  9700. $var .= '.0';
  9701. }
  9702. return "$var\n";
  9703. } elseif (is_string($var)) {
  9704. if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) {
  9705. $s = htmlSpecialChars(substr($var, 0, Debugger::$maxLen), ENT_NOQUOTES) . ' ... ';
  9706. } else {
  9707. $s = htmlSpecialChars($var, ENT_NOQUOTES);
  9708. }
  9709. $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf);
  9710. $len = strlen($var);
  9711. return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n";
  9712. } elseif (is_array($var)) {
  9713. $s = "<span>array</span>(" . count($var) . ") ";
  9714. $space = str_repeat($space1 = ' ', $level);
  9715. $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}";
  9716. static $marker;
  9717. if ($marker === NULL) {
  9718. $marker = uniqid("\x00", TRUE);
  9719. }
  9720. if (empty($var)) {
  9721. } elseif (isset($var[$marker])) {
  9722. $brackets = $var[$marker];
  9723. $s .= "$brackets[0] *RECURSION* $brackets[1]";
  9724. } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) {
  9725. $s .= "<code>$brackets[0]\n";
  9726. $var[$marker] = $brackets;
  9727. foreach ($var as $k => &$v) {
  9728. if ($k === $marker) {
  9729. continue;
  9730. }
  9731. $k = is_int($k) ? $k : '"' . htmlSpecialChars(strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf)) . '"';
  9732. $s .= "$space$space1$k => " . self::htmlDump($v, $level + 1);
  9733. }
  9734. unset($var[$marker]);
  9735. $s .= "$space$brackets[1]</code>";
  9736. } else {
  9737. $s .= "$brackets[0] ... $brackets[1]";
  9738. }
  9739. return $s . "\n";
  9740. } elseif (is_object($var)) {
  9741. $arr = (array) $var;
  9742. $s = "<span>" . get_class($var) . "</span>(" . count($arr) . ") ";
  9743. $space = str_repeat($space1 = ' ', $level);
  9744. static $list = array();
  9745. if (empty($arr)) {
  9746. } elseif (in_array($var, $list, TRUE)) {
  9747. $s .= "{ *RECURSION* }";
  9748. } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) {
  9749. $s .= "<code>{\n";
  9750. $list[] = $var;
  9751. foreach ($arr as $k => &$v) {
  9752. $m = '';
  9753. if ($k[0] === "\x00") {
  9754. $m = $k[1] === '*' ? ' <span>protected</span>' : ' <span>private</span>';
  9755. $k = substr($k, strrpos($k, "\x00") + 1);
  9756. }
  9757. $k = htmlSpecialChars(strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf));
  9758. $s .= "$space$space1\"$k\"$m => " . self::htmlDump($v, $level + 1);
  9759. }
  9760. array_pop($list);
  9761. $s .= "$space}</code>";
  9762. } else {
  9763. $s .= "{ ... }";
  9764. }
  9765. return $s . "\n";
  9766. } elseif (is_resource($var)) {
  9767. return "<span>" . htmlSpecialChars(get_resource_type($var)) . " resource</span>\n";
  9768. } else {
  9769. return "<span>unknown type</span>\n";
  9770. }
  9771. }
  9772. static function clickableDump($dump)
  9773. {
  9774. return '<pre class="nette-dump">' . preg_replace_callback(
  9775. '#^( *)((?>[^(]{1,200}))\((\d+)\) <code>#m',
  9776. function ($m) {
  9777. return "$m[1]<a href='#' rel='next'>$m[2]($m[3]) "
  9778. . (trim($m[1]) || $m[3] < 7
  9779. ? '<abbr>&#x25bc;</abbr> </a><code>'
  9780. : '<abbr>&#x25ba;</abbr> </a><code class="nette-collapsed">');
  9781. },
  9782. self::htmlDump($dump)
  9783. ) . '</pre>';
  9784. }
  9785. }
  9786. }
  9787. namespace Nette\Forms {
  9788. use Nette;
  9789. class ControlGroup extends Nette\Object
  9790. {
  9791. protected $controls;
  9792. private $options = array();
  9793. function __construct()
  9794. {
  9795. $this->controls = new \SplObjectStorage;
  9796. }
  9797. function add()
  9798. {
  9799. foreach (func_get_args() as $num => $item) {
  9800. if ($item instanceof IControl) {
  9801. $this->controls->attach($item);
  9802. } elseif ($item instanceof \Traversable || is_array($item)) {
  9803. foreach ($item as $control) {
  9804. $this->controls->attach($control);
  9805. }
  9806. } else {
  9807. throw new Nette\InvalidArgumentException("Only IFormControl items are allowed, the #$num parameter is invalid.");
  9808. }
  9809. }
  9810. return $this;
  9811. }
  9812. function getControls()
  9813. {
  9814. return iterator_to_array($this->controls);
  9815. }
  9816. function setOption($key, $value)
  9817. {
  9818. if ($value === NULL) {
  9819. unset($this->options[$key]);
  9820. } else {
  9821. $this->options[$key] = $value;
  9822. }
  9823. return $this;
  9824. }
  9825. final function getOption($key, $default = NULL)
  9826. {
  9827. return isset($this->options[$key]) ? $this->options[$key] : $default;
  9828. }
  9829. final function getOptions()
  9830. {
  9831. return $this->options;
  9832. }
  9833. }
  9834. }
  9835. namespace Nette\Forms\Controls {
  9836. use Nette;use Nette\Forms\IControl;use Nette\Utils\Html;use Nette\Forms\Form;use Nette\Forms\Rule;
  9837. abstract class BaseControl extends Nette\ComponentModel\Component implements IControl
  9838. {
  9839. public static $idMask = 'frm%s-%s';
  9840. public $caption;
  9841. protected $value;
  9842. protected $control;
  9843. protected $label;
  9844. private $errors = array();
  9845. private $disabled = FALSE;
  9846. private $htmlId;
  9847. private $htmlName;
  9848. private $rules;
  9849. private $translator = TRUE;
  9850. private $options = array();
  9851. function __construct($caption = NULL)
  9852. {
  9853. $this->monitor('Nette\Forms\Form');
  9854. parent::__construct();
  9855. $this->control = Html::el('input');
  9856. $this->label = Html::el('label');
  9857. $this->caption = $caption;
  9858. $this->rules = new Nette\Forms\Rules($this);
  9859. }
  9860. protected function attached($form)
  9861. {
  9862. if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) {
  9863. $this->htmlName = NULL;
  9864. $this->loadHttpData();
  9865. }
  9866. }
  9867. function getForm($need = TRUE)
  9868. {
  9869. return $this->lookup('Nette\Forms\Form', $need);
  9870. }
  9871. function getHtmlName()
  9872. {
  9873. if ($this->htmlName === NULL) {
  9874. $name = str_replace(self::NAME_SEPARATOR, '][', $this->lookupPath('Nette\Forms\Form'), $count);
  9875. if ($count) {
  9876. $name = substr_replace($name, '', strpos($name, ']'), 1) . ']';
  9877. }
  9878. if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) {
  9879. $name .= '_';
  9880. }
  9881. $this->htmlName = $name;
  9882. }
  9883. return $this->htmlName;
  9884. }
  9885. function setHtmlId($id)
  9886. {
  9887. $this->htmlId = $id;
  9888. return $this;
  9889. }
  9890. function getHtmlId()
  9891. {
  9892. if ($this->htmlId === FALSE) {
  9893. return NULL;
  9894. } elseif ($this->htmlId === NULL) {
  9895. $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->lookupPath('Nette\Forms\Form'));
  9896. }
  9897. return $this->htmlId;
  9898. }
  9899. function setAttribute($name, $value = TRUE)
  9900. {
  9901. $this->control->$name = $value;
  9902. return $this;
  9903. }
  9904. function setOption($key, $value)
  9905. {
  9906. if ($value === NULL) {
  9907. unset($this->options[$key]);
  9908. } else {
  9909. $this->options[$key] = $value;
  9910. }
  9911. return $this;
  9912. }
  9913. final function getOption($key, $default = NULL)
  9914. {
  9915. return isset($this->options[$key]) ? $this->options[$key] : $default;
  9916. }
  9917. final function getOptions()
  9918. {
  9919. return $this->options;
  9920. }
  9921. function setTranslator(Nette\Localization\ITranslator $translator = NULL)
  9922. {
  9923. $this->translator = $translator;
  9924. return $this;
  9925. }
  9926. final function getTranslator()
  9927. {
  9928. if ($this->translator === TRUE) {
  9929. return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL;
  9930. }
  9931. return $this->translator;
  9932. }
  9933. function translate($s, $count = NULL)
  9934. {
  9935. $translator = $this->getTranslator();
  9936. return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count);
  9937. }
  9938. function setValue($value)
  9939. {
  9940. $this->value = $value;
  9941. return $this;
  9942. }
  9943. function getValue()
  9944. {
  9945. return $this->value;
  9946. }
  9947. function isFilled()
  9948. {
  9949. return (string) $this->getValue() !== '';
  9950. }
  9951. function setDefaultValue($value)
  9952. {
  9953. $form = $this->getForm(FALSE);
  9954. if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
  9955. $this->setValue($value);
  9956. }
  9957. return $this;
  9958. }
  9959. function loadHttpData()
  9960. {
  9961. $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_'));
  9962. $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL));
  9963. }
  9964. function setDisabled($value = TRUE)
  9965. {
  9966. $this->disabled = (bool) $value;
  9967. return $this;
  9968. }
  9969. function isDisabled()
  9970. {
  9971. return $this->disabled;
  9972. }
  9973. function getControl()
  9974. {
  9975. $this->setOption('rendered', TRUE);
  9976. $control = clone $this->control;
  9977. $control->name = $this->getHtmlName();
  9978. $control->disabled = $this->disabled;
  9979. $control->id = $this->getHtmlId();
  9980. $control->required = $this->isRequired();
  9981. $rules = self::exportRules($this->rules);
  9982. $rules = substr(json_encode($rules), 1, -1);
  9983. $rules = preg_replace('#"([a-z0-9]+)":#i', '$1:', $rules);
  9984. $rules = preg_replace('#(?<!\\\\)"([^\\\\\',]*)"#i', "'$1'", $rules);
  9985. $control->data('nette-rules', $rules ? $rules : NULL);
  9986. return $control;
  9987. }
  9988. function getLabel($caption = NULL)
  9989. {
  9990. $label = clone $this->label;
  9991. $label->for = $this->getHtmlId();
  9992. if ($caption !== NULL) {
  9993. $label->setText($this->translate($caption));
  9994. } elseif ($this->caption instanceof Html) {
  9995. $label->add($this->caption);
  9996. } else {
  9997. $label->setText($this->translate($this->caption));
  9998. }
  9999. return $label;
  10000. }
  10001. final function getControlPrototype()
  10002. {
  10003. return $this->control;
  10004. }
  10005. final function getLabelPrototype()
  10006. {
  10007. return $this->label;
  10008. }
  10009. function addRule($operation, $message = NULL, $arg = NULL)
  10010. {
  10011. $this->rules->addRule($operation, $message, $arg);
  10012. return $this;
  10013. }
  10014. function addCondition($operation, $value = NULL)
  10015. {
  10016. return $this->rules->addCondition($operation, $value);
  10017. }
  10018. function addConditionOn(IControl $control, $operation, $value = NULL)
  10019. {
  10020. return $this->rules->addConditionOn($control, $operation, $value);
  10021. }
  10022. final function getRules()
  10023. {
  10024. return $this->rules;
  10025. }
  10026. final function setRequired($message = NULL)
  10027. {
  10028. return $this->addRule(Form::FILLED, $message);
  10029. }
  10030. final function isRequired()
  10031. {
  10032. foreach ($this->rules as $rule) {
  10033. if ($rule->type === Rule::VALIDATOR && !$rule->isNegative && $rule->operation === Form::FILLED) {
  10034. return TRUE;
  10035. }
  10036. }
  10037. return FALSE;
  10038. }
  10039. private static function exportRules($rules)
  10040. {
  10041. $payload = array();
  10042. foreach ($rules as $rule) {
  10043. if (!is_string($op = $rule->operation)) {
  10044. $op = callback($op);
  10045. if (!$op->isStatic()) {
  10046. continue;
  10047. }
  10048. }
  10049. if ($rule->type === Rule::VALIDATOR) {
  10050. $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE));
  10051. } elseif ($rule->type === Rule::CONDITION) {
  10052. $item = array(
  10053. 'op' => ($rule->isNegative ? '~' : '') . $op,
  10054. 'rules' => self::exportRules($rule->subRules),
  10055. 'control' => $rule->control->getHtmlName()
  10056. );
  10057. if ($rule->subRules->getToggles()) {
  10058. $item['toggle'] = $rule->subRules->getToggles();
  10059. }
  10060. }
  10061. if (is_array($rule->arg)) {
  10062. foreach ($rule->arg as $key => $value) {
  10063. $item['arg'][$key] = $value instanceof IControl ? (object) array('control' => $value->getHtmlName()) : $value;
  10064. }
  10065. } elseif ($rule->arg !== NULL) {
  10066. $item['arg'] = $rule->arg instanceof IControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg;
  10067. }
  10068. $payload[] = $item;
  10069. }
  10070. return $payload;
  10071. }
  10072. static function validateEqual(IControl $control, $arg)
  10073. {
  10074. $value = $control->getValue();
  10075. foreach ((is_array($value) ? $value : array($value)) as $val) {
  10076. foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
  10077. if ((string) $val === (string) ($item instanceof IControl ? $item->value : $item)) {
  10078. return TRUE;
  10079. }
  10080. }
  10081. }
  10082. return FALSE;
  10083. }
  10084. static function validateFilled(IControl $control)
  10085. {
  10086. return $control->isFilled();
  10087. }
  10088. static function validateValid(IControl $control)
  10089. {
  10090. return $control->rules->validate(TRUE);
  10091. }
  10092. function addError($message)
  10093. {
  10094. if (!in_array($message, $this->errors, TRUE)) {
  10095. $this->errors[] = $message;
  10096. }
  10097. $this->getForm()->addError($message);
  10098. }
  10099. function getErrors()
  10100. {
  10101. return $this->errors;
  10102. }
  10103. function hasErrors()
  10104. {
  10105. return (bool) $this->errors;
  10106. }
  10107. function cleanErrors()
  10108. {
  10109. $this->errors = array();
  10110. }
  10111. }
  10112. class Button extends BaseControl
  10113. {
  10114. function __construct($caption = NULL)
  10115. {
  10116. parent::__construct($caption);
  10117. $this->control->type = 'button';
  10118. }
  10119. function getLabel($caption = NULL)
  10120. {
  10121. return NULL;
  10122. }
  10123. function getControl($caption = NULL)
  10124. {
  10125. $control = parent::getControl();
  10126. $control->value = $this->translate($caption === NULL ? $this->caption : $caption);
  10127. return $control;
  10128. }
  10129. }
  10130. class Checkbox extends BaseControl
  10131. {
  10132. function __construct($label = NULL)
  10133. {
  10134. parent::__construct($label);
  10135. $this->control->type = 'checkbox';
  10136. $this->value = FALSE;
  10137. }
  10138. function setValue($value)
  10139. {
  10140. $this->value = is_scalar($value) ? (bool) $value : FALSE;
  10141. return $this;
  10142. }
  10143. function getControl()
  10144. {
  10145. return parent::getControl()->checked($this->value);
  10146. }
  10147. }
  10148. class HiddenField extends BaseControl
  10149. {
  10150. private $forcedValue;
  10151. function __construct($forcedValue = NULL)
  10152. {
  10153. parent::__construct();
  10154. $this->control->type = 'hidden';
  10155. $this->value = (string) $forcedValue;
  10156. $this->forcedValue = $forcedValue;
  10157. }
  10158. function getLabel($caption = NULL)
  10159. {
  10160. return NULL;
  10161. }
  10162. function setValue($value)
  10163. {
  10164. $this->value = is_scalar($value) ? (string) $value : '';
  10165. return $this;
  10166. }
  10167. function getControl()
  10168. {
  10169. return parent::getControl()
  10170. ->value($this->forcedValue === NULL ? $this->value : $this->forcedValue)
  10171. ->data('nette-rules', NULL);
  10172. }
  10173. }
  10174. class SubmitButton extends Button implements Nette\Forms\ISubmitterControl
  10175. {
  10176. public $onClick;
  10177. public $onInvalidClick;
  10178. private $validationScope = TRUE;
  10179. function __construct($caption = NULL)
  10180. {
  10181. parent::__construct($caption);
  10182. $this->control->type = 'submit';
  10183. }
  10184. function setValue($value)
  10185. {
  10186. $this->value = is_scalar($value) && (bool) $value;
  10187. $form = $this->getForm();
  10188. if ($this->value || !is_object($form->isSubmitted())) {
  10189. $this->value = TRUE;
  10190. $form->setSubmittedBy($this);
  10191. }
  10192. return $this;
  10193. }
  10194. function isSubmittedBy()
  10195. {
  10196. return $this->getForm()->isSubmitted() === $this;
  10197. }
  10198. function setValidationScope($scope)
  10199. {
  10200. $this->validationScope = (bool) $scope;
  10201. $this->control->formnovalidate = !$this->validationScope;
  10202. return $this;
  10203. }
  10204. final function getValidationScope()
  10205. {
  10206. return $this->validationScope;
  10207. }
  10208. function click()
  10209. {
  10210. $this->onClick($this);
  10211. }
  10212. static function validateSubmitted(Nette\Forms\ISubmitterControl $control)
  10213. {
  10214. return $control->isSubmittedBy();
  10215. }
  10216. }
  10217. class ImageButton extends SubmitButton
  10218. {
  10219. function __construct($src = NULL, $alt = NULL)
  10220. {
  10221. parent::__construct();
  10222. $this->control->type = 'image';
  10223. $this->control->src = $src;
  10224. $this->control->alt = $alt;
  10225. }
  10226. function getHtmlName()
  10227. {
  10228. $name = parent::getHtmlName();
  10229. return strpos($name, '[') === FALSE ? $name : $name . '[]';
  10230. }
  10231. function loadHttpData()
  10232. {
  10233. $path = $this->getHtmlName();
  10234. $path = explode('[', strtr(str_replace(']', '', strpos($path, '[') === FALSE ? $path . '.x' : substr($path, 0, -2)), '.', '_'));
  10235. $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL) !== NULL);
  10236. }
  10237. }
  10238. class SelectBox extends BaseControl
  10239. {
  10240. private $items = array();
  10241. protected $allowed = array();
  10242. private $prompt = FALSE;
  10243. private $useKeys = TRUE;
  10244. function __construct($label = NULL, array $items = NULL, $size = NULL)
  10245. {
  10246. parent::__construct($label);
  10247. $this->control->setName('select');
  10248. $this->control->size = $size > 1 ? (int) $size : NULL;
  10249. if ($items !== NULL) {
  10250. $this->setItems($items);
  10251. }
  10252. }
  10253. function getValue()
  10254. {
  10255. $allowed = $this->allowed;
  10256. if ($this->prompt) {
  10257. $allowed = array_slice($allowed, 1, count($allowed), TRUE);
  10258. }
  10259. return is_scalar($this->value) && isset($allowed[$this->value]) ? $this->value : NULL;
  10260. }
  10261. function getRawValue()
  10262. {
  10263. return is_scalar($this->value) ? $this->value : NULL;
  10264. }
  10265. function isFilled()
  10266. {
  10267. $value = $this->getValue();
  10268. return is_array($value) ? count($value) > 0 : $value !== NULL;
  10269. }
  10270. function setPrompt($prompt)
  10271. {
  10272. if (is_bool($prompt)) {
  10273. $this->prompt = $prompt;
  10274. } else {
  10275. $this->prompt = TRUE;
  10276. if ($prompt !== NULL) {
  10277. $this->items = array('' => $prompt) + $this->items;
  10278. $this->allowed = array('' => '') + $this->allowed;
  10279. }
  10280. }
  10281. return $this;
  10282. }
  10283. function skipFirst($v = NULL)
  10284. {
  10285. trigger_error(__METHOD__ . '() is deprecated; use setPrompt() instead.', E_USER_WARNING);
  10286. return $this->setPrompt($v);
  10287. }
  10288. final function getPrompt()
  10289. {
  10290. return $this->prompt;
  10291. }
  10292. final function areKeysUsed()
  10293. {
  10294. return $this->useKeys;
  10295. }
  10296. function setItems(array $items, $useKeys = TRUE)
  10297. {
  10298. $this->items = $items;
  10299. $this->allowed = array();
  10300. $this->useKeys = (bool) $useKeys;
  10301. foreach ($items as $key => $value) {
  10302. if (!is_array($value)) {
  10303. $value = array($key => $value);
  10304. }
  10305. foreach ($value as $key2 => $value2) {
  10306. if (!$this->useKeys) {
  10307. if (!is_scalar($value2)) {
  10308. throw new Nette\InvalidArgumentException("All items must be scalar.");
  10309. }
  10310. $key2 = $value2;
  10311. }
  10312. if (isset($this->allowed[$key2])) {
  10313. throw new Nette\InvalidArgumentException("Items contain duplication for key '$key2'.");
  10314. }
  10315. $this->allowed[$key2] = $value2;
  10316. }
  10317. }
  10318. return $this;
  10319. }
  10320. final function getItems()
  10321. {
  10322. return $this->items;
  10323. }
  10324. function getSelectedItem()
  10325. {
  10326. if (!$this->useKeys) {
  10327. return $this->getValue();
  10328. } else {
  10329. $value = $this->getValue();
  10330. return $value === NULL ? NULL : $this->allowed[$value];
  10331. }
  10332. }
  10333. function getControl()
  10334. {
  10335. $control = parent::getControl();
  10336. if ($this->prompt) {
  10337. reset($this->items);
  10338. $control->data('nette-empty-value', $this->useKeys ? key($this->items) : current($this->items));
  10339. }
  10340. $selected = $this->getValue();
  10341. $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE);
  10342. $option = Nette\Utils\Html::el('option');
  10343. foreach ($this->items as $key => $value) {
  10344. if (!is_array($value)) {
  10345. $value = array($key => $value);
  10346. $dest = $control;
  10347. } else {
  10348. $dest = $control->create('optgroup')->label($this->translate($key));
  10349. }
  10350. foreach ($value as $key2 => $value2) {
  10351. if ($value2 instanceof Nette\Utils\Html) {
  10352. $dest->add((string) $value2->selected(isset($selected[$key2])));
  10353. } else {
  10354. $key2 = $this->useKeys ? $key2 : $value2;
  10355. $value2 = $this->translate((string) $value2);
  10356. $dest->add((string) $option->value($key2 === $value2 ? NULL : $key2)
  10357. ->selected(isset($selected[$key2]))
  10358. ->setText($value2));
  10359. }
  10360. }
  10361. }
  10362. return $control;
  10363. }
  10364. }
  10365. class MultiSelectBox extends SelectBox
  10366. {
  10367. function getValue()
  10368. {
  10369. $allowed = array_keys($this->allowed);
  10370. if ($this->getPrompt()) {
  10371. unset($allowed[0]);
  10372. }
  10373. return array_intersect($this->getRawValue(), $allowed);
  10374. }
  10375. function getRawValue()
  10376. {
  10377. if (is_scalar($this->value)) {
  10378. $value = array($this->value);
  10379. } elseif (!is_array($this->value)) {
  10380. $value = array();
  10381. } else {
  10382. $value = $this->value;
  10383. }
  10384. $res = array();
  10385. foreach ($value as $val) {
  10386. if (is_scalar($val)) {
  10387. $res[] = $val;
  10388. }
  10389. }
  10390. return $res;
  10391. }
  10392. function getSelectedItem()
  10393. {
  10394. if (!$this->areKeysUsed()) {
  10395. return $this->getValue();
  10396. } else {
  10397. $res = array();
  10398. foreach ($this->getValue() as $value) {
  10399. $res[$value] = $this->allowed[$value];
  10400. }
  10401. return $res;
  10402. }
  10403. }
  10404. function getHtmlName()
  10405. {
  10406. return parent::getHtmlName() . '[]';
  10407. }
  10408. function getControl()
  10409. {
  10410. $control = parent::getControl();
  10411. $control->multiple = TRUE;
  10412. return $control;
  10413. }
  10414. }
  10415. class RadioList extends BaseControl
  10416. {
  10417. protected $separator;
  10418. protected $container;
  10419. protected $items = array();
  10420. function __construct($label = NULL, array $items = NULL)
  10421. {
  10422. parent::__construct($label);
  10423. $this->control->type = 'radio';
  10424. $this->container = Html::el();
  10425. $this->separator = Html::el('br');
  10426. if ($items !== NULL) {
  10427. $this->setItems($items);
  10428. }
  10429. }
  10430. function getValue($raw = FALSE)
  10431. {
  10432. return is_scalar($this->value) && ($raw || isset($this->items[$this->value])) ? $this->value : NULL;
  10433. }
  10434. function isFilled()
  10435. {
  10436. return $this->getValue() !== NULL;
  10437. }
  10438. function setItems(array $items)
  10439. {
  10440. $this->items = $items;
  10441. return $this;
  10442. }
  10443. final function getItems()
  10444. {
  10445. return $this->items;
  10446. }
  10447. final function getSeparatorPrototype()
  10448. {
  10449. return $this->separator;
  10450. }
  10451. final function getContainerPrototype()
  10452. {
  10453. return $this->container;
  10454. }
  10455. function getControl($key = NULL)
  10456. {
  10457. if ($key === NULL) {
  10458. $container = clone $this->container;
  10459. $separator = (string) $this->separator;
  10460. } elseif (!isset($this->items[$key])) {
  10461. return NULL;
  10462. }
  10463. $control = parent::getControl();
  10464. $id = $control->id;
  10465. $counter = -1;
  10466. $value = $this->value === NULL ? NULL : (string) $this->getValue();
  10467. $label = Html::el('label');
  10468. foreach ($this->items as $k => $val) {
  10469. $counter++;
  10470. if ($key !== NULL && $key != $k) {
  10471. continue;
  10472. }
  10473. $control->id = $label->for = $id . '-' . $counter;
  10474. $control->checked = (string) $k === $value;
  10475. $control->value = $k;
  10476. if ($val instanceof Html) {
  10477. $label->setHtml($val);
  10478. } else {
  10479. $label->setText($this->translate((string) $val));
  10480. }
  10481. if ($key !== NULL) {
  10482. return Html::el()->add($control)->add($label);
  10483. }
  10484. $container->add((string) $control . (string) $label . $separator);
  10485. $control->data('nette-rules', NULL);
  10486. }
  10487. return $container;
  10488. }
  10489. function getLabel($caption = NULL)
  10490. {
  10491. $label = parent::getLabel($caption);
  10492. $label->for = NULL;
  10493. return $label;
  10494. }
  10495. }
  10496. use Nette\Utils\Strings;
  10497. abstract class TextBase extends BaseControl
  10498. {
  10499. protected $emptyValue = '';
  10500. protected $filters = array();
  10501. function setValue($value)
  10502. {
  10503. $this->value = is_scalar($value) ? (string) $value : '';
  10504. return $this;
  10505. }
  10506. function getValue()
  10507. {
  10508. $value = $this->value;
  10509. foreach ($this->filters as $filter) {
  10510. $value = (string) $filter($value);
  10511. }
  10512. return $value === $this->translate($this->emptyValue) ? '' : $value;
  10513. }
  10514. function setEmptyValue($value)
  10515. {
  10516. $this->emptyValue = (string) $value;
  10517. return $this;
  10518. }
  10519. final function getEmptyValue()
  10520. {
  10521. return $this->emptyValue;
  10522. }
  10523. function addFilter($filter)
  10524. {
  10525. $this->filters[] = callback($filter);
  10526. return $this;
  10527. }
  10528. function getControl()
  10529. {
  10530. $control = parent::getControl();
  10531. foreach ($this->getRules() as $rule) {
  10532. if ($rule->type === Nette\Forms\Rule::VALIDATOR && !$rule->isNegative
  10533. && ($rule->operation === Form::LENGTH || $rule->operation === Form::MAX_LENGTH)
  10534. ) {
  10535. $control->maxlength = is_array($rule->arg) ? $rule->arg[1] : $rule->arg;
  10536. }
  10537. }
  10538. if ($this->emptyValue !== '') {
  10539. $control->data('nette-empty-value', $this->translate($this->emptyValue));
  10540. }
  10541. return $control;
  10542. }
  10543. function addRule($operation, $message = NULL, $arg = NULL)
  10544. {
  10545. if ($operation === Form::FLOAT) {
  10546. $this->addFilter(callback(__CLASS__, 'filterFloat'));
  10547. }
  10548. return parent::addRule($operation, $message, $arg);
  10549. }
  10550. static function validateMinLength(TextBase $control, $length)
  10551. {
  10552. return Strings::length($control->getValue()) >= $length;
  10553. }
  10554. static function validateMaxLength(TextBase $control, $length)
  10555. {
  10556. return Strings::length($control->getValue()) <= $length;
  10557. }
  10558. static function validateLength(TextBase $control, $range)
  10559. {
  10560. if (!is_array($range)) {
  10561. $range = array($range, $range);
  10562. }
  10563. $len = Strings::length($control->getValue());
  10564. return ($range[0] === NULL || $len >= $range[0]) && ($range[1] === NULL || $len <= $range[1]);
  10565. }
  10566. static function validateEmail(TextBase $control)
  10567. {
  10568. $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]";
  10569. $localPart = "(?:\"(?:[ !\\x23-\\x5B\\x5D-\\x7E]*|\\\\[ -~])+\"|$atom+(?:\\.$atom+)*)";
  10570. $chars = "a-z0-9\x80-\xFF";
  10571. $domain = "[$chars](?:[-$chars]{0,61}[$chars])";
  10572. return (bool) Strings::match($control->getValue(), "(^$localPart@(?:$domain?\\.)+[-$chars]{2,19}\\z)i");
  10573. }
  10574. static function validateUrl(TextBase $control)
  10575. {
  10576. $chars = "a-z0-9\x80-\xFF";
  10577. return (bool) Strings::match(
  10578. $control->getValue(),
  10579. "#^(?:https?://|)(?:[$chars](?:[-$chars]{0,61}[$chars])?\\.)+[-$chars]{2,19}(/\S*)?$#i"
  10580. );
  10581. }
  10582. static function validateRegexp(TextBase $control, $regexp)
  10583. {
  10584. return (bool) Strings::match($control->getValue(), $regexp);
  10585. }
  10586. static function validatePattern(TextBase $control, $pattern)
  10587. {
  10588. return (bool) Strings::match($control->getValue(), "\x01^($pattern)$\x01u");
  10589. }
  10590. static function validateInteger(TextBase $control)
  10591. {
  10592. return (bool) Strings::match($control->getValue(), '/^-?[0-9]+$/');
  10593. }
  10594. static function validateFloat(TextBase $control)
  10595. {
  10596. return (bool) Strings::match($control->getValue(), '/^-?[0-9]*[.,]?[0-9]+$/');
  10597. }
  10598. static function validateRange(TextBase $control, $range)
  10599. {
  10600. return ($range[0] === NULL || $control->getValue() >= $range[0])
  10601. && ($range[1] === NULL || $control->getValue() <= $range[1]);
  10602. }
  10603. static function filterFloat($s)
  10604. {
  10605. return str_replace(array(' ', ','), array('', '.'), $s);
  10606. }
  10607. }
  10608. class TextArea extends TextBase
  10609. {
  10610. function __construct($label = NULL, $cols = NULL, $rows = NULL)
  10611. {
  10612. parent::__construct($label);
  10613. $this->control->setName('textarea');
  10614. $this->control->cols = $cols;
  10615. $this->control->rows = $rows;
  10616. $this->value = '';
  10617. }
  10618. function getControl()
  10619. {
  10620. $control = parent::getControl();
  10621. $control->setText($this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value);
  10622. return $control;
  10623. }
  10624. }
  10625. class TextInput extends TextBase
  10626. {
  10627. function __construct($label = NULL, $cols = NULL, $maxLength = NULL)
  10628. {
  10629. parent::__construct($label);
  10630. $this->control->type = 'text';
  10631. $this->control->size = $cols;
  10632. $this->control->maxlength = $maxLength;
  10633. $this->filters[] = callback($this, 'sanitize');
  10634. $this->value = '';
  10635. }
  10636. function sanitize($value)
  10637. {
  10638. if ($this->control->maxlength && Nette\Utils\Strings::length($value) > $this->control->maxlength) {
  10639. $value = iconv_substr($value, 0, $this->control->maxlength, 'UTF-8');
  10640. }
  10641. return Nette\Utils\Strings::trim(strtr($value, "\r\n", ' '));
  10642. }
  10643. function setType($type)
  10644. {
  10645. $this->control->type = $type;
  10646. return $this;
  10647. }
  10648. function setPasswordMode($mode = TRUE)
  10649. {
  10650. $this->control->type = $mode ? 'password' : 'text';
  10651. return $this;
  10652. }
  10653. function getControl()
  10654. {
  10655. $control = parent::getControl();
  10656. foreach ($this->getRules() as $rule) {
  10657. if ($rule->isNegative || $rule->type !== Nette\Forms\Rule::VALIDATOR) {
  10658. } elseif ($rule->operation === Nette\Forms\Form::RANGE && $control->type !== 'text') {
  10659. list($control->min, $control->max) = $rule->arg;
  10660. } elseif ($rule->operation === Nette\Forms\Form::PATTERN) {
  10661. $control->pattern = $rule->arg;
  10662. }
  10663. }
  10664. if ($control->type !== 'password') {
  10665. $control->value = $this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value;
  10666. }
  10667. return $control;
  10668. }
  10669. }
  10670. use Nette\Http;
  10671. class UploadControl extends BaseControl
  10672. {
  10673. function __construct($label = NULL)
  10674. {
  10675. parent::__construct($label);
  10676. $this->control->type = 'file';
  10677. }
  10678. protected function attached($form)
  10679. {
  10680. if ($form instanceof Nette\Forms\Form) {
  10681. if ($form->getMethod() !== Nette\Forms\Form::POST) {
  10682. throw new Nette\InvalidStateException('File upload requires method POST.');
  10683. }
  10684. $form->getElementPrototype()->enctype = 'multipart/form-data';
  10685. }
  10686. parent::attached($form);
  10687. }
  10688. function setValue($value)
  10689. {
  10690. if (is_array($value)) {
  10691. $this->value = new Http\FileUpload($value);
  10692. } elseif ($value instanceof Http\FileUpload) {
  10693. $this->value = $value;
  10694. } else {
  10695. $this->value = new Http\FileUpload(NULL);
  10696. }
  10697. return $this;
  10698. }
  10699. function isFilled()
  10700. {
  10701. return $this->value instanceof Http\FileUpload && $this->value->isOK();
  10702. }
  10703. static function validateFileSize(UploadControl $control, $limit)
  10704. {
  10705. $file = $control->getValue();
  10706. return $file instanceof Http\FileUpload && $file->getSize() <= $limit;
  10707. }
  10708. static function validateMimeType(UploadControl $control, $mimeType)
  10709. {
  10710. $file = $control->getValue();
  10711. if ($file instanceof Http\FileUpload) {
  10712. $type = strtolower($file->getContentType());
  10713. $mimeTypes = is_array($mimeType) ? $mimeType : explode(',', $mimeType);
  10714. if (in_array($type, $mimeTypes, TRUE)) {
  10715. return TRUE;
  10716. }
  10717. if (in_array(preg_replace('#/.*#', '/*', $type), $mimeTypes, TRUE)) {
  10718. return TRUE;
  10719. }
  10720. }
  10721. return FALSE;
  10722. }
  10723. static function validateImage(UploadControl $control)
  10724. {
  10725. $file = $control->getValue();
  10726. return $file instanceof Http\FileUpload && $file->isImage();
  10727. }
  10728. }
  10729. }
  10730. namespace Nette\Forms\Rendering {
  10731. use Nette;use Nette\Utils\Html;
  10732. class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
  10733. {
  10734. public $wrappers = array(
  10735. 'form' => array(
  10736. 'container' => NULL,
  10737. 'errors' => TRUE,
  10738. ),
  10739. 'error' => array(
  10740. 'container' => 'ul class=error',
  10741. 'item' => 'li',
  10742. ),
  10743. 'group' => array(
  10744. 'container' => 'fieldset',
  10745. 'label' => 'legend',
  10746. 'description' => 'p',
  10747. ),
  10748. 'controls' => array(
  10749. 'container' => 'table',
  10750. ),
  10751. 'pair' => array(
  10752. 'container' => 'tr',
  10753. '.required' => 'required',
  10754. '.optional' => NULL,
  10755. '.odd' => NULL,
  10756. ),
  10757. 'control' => array(
  10758. 'container' => 'td',
  10759. '.odd' => NULL,
  10760. 'errors' => FALSE,
  10761. 'description' => 'small',
  10762. 'requiredsuffix' => '',
  10763. '.required' => 'required',
  10764. '.text' => 'text',
  10765. '.password' => 'text',
  10766. '.file' => 'text',
  10767. '.submit' => 'button',
  10768. '.image' => 'imagebutton',
  10769. '.button' => 'button',
  10770. ),
  10771. 'label' => array(
  10772. 'container' => 'th',
  10773. 'suffix' => NULL,
  10774. 'requiredsuffix' => '',
  10775. ),
  10776. 'hidden' => array(
  10777. 'container' => 'div',
  10778. ),
  10779. );
  10780. protected $form;
  10781. protected $counter;
  10782. function render(Nette\Forms\Form $form, $mode = NULL)
  10783. {
  10784. if ($this->form !== $form) {
  10785. $this->form = $form;
  10786. $this->init();
  10787. }
  10788. $s = '';
  10789. if (!$mode || $mode === 'begin') {
  10790. $s .= $this->renderBegin();
  10791. }
  10792. if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
  10793. $s .= $this->renderErrors();
  10794. }
  10795. if (!$mode || $mode === 'body') {
  10796. $s .= $this->renderBody();
  10797. }
  10798. if (!$mode || $mode === 'end') {
  10799. $s .= $this->renderEnd();
  10800. }
  10801. return $s;
  10802. }
  10803. function setClientScript()
  10804. {
  10805. trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
  10806. return $this;
  10807. }
  10808. protected function init()
  10809. {
  10810. $wrapper = & $this->wrappers['control'];
  10811. foreach ($this->form->getControls() as $control) {
  10812. if ($control->isRequired() && isset($wrapper['.required'])) {
  10813. $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
  10814. }
  10815. $el = $control->getControlPrototype();
  10816. if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
  10817. $el->class($wrapper['.' . $el->type], TRUE);
  10818. }
  10819. }
  10820. }
  10821. function renderBegin()
  10822. {
  10823. $this->counter = 0;
  10824. foreach ($this->form->getControls() as $control) {
  10825. $control->setOption('rendered', FALSE);
  10826. }
  10827. if (strcasecmp($this->form->getMethod(), 'get') === 0) {
  10828. $el = clone $this->form->getElementPrototype();
  10829. $url = explode('?', (string) $el->action, 2);
  10830. $el->action = $url[0];
  10831. $s = '';
  10832. if (isset($url[1])) {
  10833. foreach (preg_split('#[;&]#', $url[1]) as $param) {
  10834. $parts = explode('=', $param, 2);
  10835. $name = urldecode($parts[0]);
  10836. if (!isset($this->form[$name])) {
  10837. $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
  10838. }
  10839. }
  10840. $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
  10841. }
  10842. return $el->startTag() . $s;
  10843. } else {
  10844. return $this->form->getElementPrototype()->startTag();
  10845. }
  10846. }
  10847. function renderEnd()
  10848. {
  10849. $s = '';
  10850. foreach ($this->form->getControls() as $control) {
  10851. if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
  10852. $s .= (string) $control->getControl();
  10853. }
  10854. }
  10855. if ($s) {
  10856. $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
  10857. }
  10858. return $s . $this->form->getElementPrototype()->endTag() . "\n";
  10859. }
  10860. function renderErrors(Nette\Forms\IControl $control = NULL)
  10861. {
  10862. $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
  10863. if (count($errors)) {
  10864. $ul = $this->getWrapper('error container');
  10865. $li = $this->getWrapper('error item');
  10866. foreach ($errors as $error) {
  10867. $item = clone $li;
  10868. if ($error instanceof Html) {
  10869. $item->add($error);
  10870. } else {
  10871. $item->setText($error);
  10872. }
  10873. $ul->add($item);
  10874. }
  10875. return "\n" . $ul->render(0);
  10876. }
  10877. }
  10878. function renderBody()
  10879. {
  10880. $s = $remains = '';
  10881. $defaultContainer = $this->getWrapper('group container');
  10882. $translator = $this->form->getTranslator();
  10883. foreach ($this->form->getGroups() as $group) {
  10884. if (!$group->getControls() || !$group->getOption('visual')) {
  10885. continue;
  10886. }
  10887. $container = $group->getOption('container', $defaultContainer);
  10888. $container = $container instanceof Html ? clone $container : Html::el($container);
  10889. $s .= "\n" . $container->startTag();
  10890. $text = $group->getOption('label');
  10891. if ($text instanceof Html) {
  10892. $s .= $text;
  10893. } elseif (is_string($text)) {
  10894. if ($translator !== NULL) {
  10895. $text = $translator->translate($text);
  10896. }
  10897. $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
  10898. }
  10899. $text = $group->getOption('description');
  10900. if ($text instanceof Html) {
  10901. $s .= $text;
  10902. } elseif (is_string($text)) {
  10903. if ($translator !== NULL) {
  10904. $text = $translator->translate($text);
  10905. }
  10906. $s .= $this->getWrapper('group description')->setText($text) . "\n";
  10907. }
  10908. $s .= $this->renderControls($group);
  10909. $remains = $container->endTag() . "\n" . $remains;
  10910. if (!$group->getOption('embedNext')) {
  10911. $s .= $remains;
  10912. $remains = '';
  10913. }
  10914. }
  10915. $s .= $remains . $this->renderControls($this->form);
  10916. $container = $this->getWrapper('form container');
  10917. $container->setHtml($s);
  10918. return $container->render(0);
  10919. }
  10920. function renderControls($parent)
  10921. {
  10922. if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
  10923. throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
  10924. }
  10925. $container = $this->getWrapper('controls container');
  10926. $buttons = NULL;
  10927. foreach ($parent->getControls() as $control) {
  10928. if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
  10929. } elseif ($control instanceof Nette\Forms\Controls\Button) {
  10930. $buttons[] = $control;
  10931. } else {
  10932. if ($buttons) {
  10933. $container->add($this->renderPairMulti($buttons));
  10934. $buttons = NULL;
  10935. }
  10936. $container->add($this->renderPair($control));
  10937. }
  10938. }
  10939. if ($buttons) {
  10940. $container->add($this->renderPairMulti($buttons));
  10941. }
  10942. $s = '';
  10943. if (count($container)) {
  10944. $s .= "\n" . $container . "\n";
  10945. }
  10946. return $s;
  10947. }
  10948. function renderPair(Nette\Forms\IControl $control)
  10949. {
  10950. $pair = $this->getWrapper('pair container');
  10951. $pair->add($this->renderLabel($control));
  10952. $pair->add($this->renderControl($control));
  10953. $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
  10954. $pair->class($control->getOption('class'), TRUE);
  10955. if (++$this->counter % 2) {
  10956. $pair->class($this->getValue('pair .odd'), TRUE);
  10957. }
  10958. $pair->id = $control->getOption('id');
  10959. return $pair->render(0);
  10960. }
  10961. function renderPairMulti(array $controls)
  10962. {
  10963. $s = array();
  10964. foreach ($controls as $control) {
  10965. if (!$control instanceof Nette\Forms\IControl) {
  10966. throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances.");
  10967. }
  10968. $s[] = (string) $control->getControl();
  10969. }
  10970. $pair = $this->getWrapper('pair container');
  10971. $pair->add($this->renderLabel($control));
  10972. $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
  10973. return $pair->render(0);
  10974. }
  10975. function renderLabel(Nette\Forms\IControl $control)
  10976. {
  10977. $head = $this->getWrapper('label container');
  10978. if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
  10979. return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? '&nbsp;' : '');
  10980. } else {
  10981. $label = $control->getLabel();
  10982. $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
  10983. if ($label instanceof Html) {
  10984. $label->setHtml($label->getHtml() . $suffix);
  10985. $suffix = '';
  10986. }
  10987. return $head->setHtml((string) $label . $suffix);
  10988. }
  10989. }
  10990. function renderControl(Nette\Forms\IControl $control)
  10991. {
  10992. $body = $this->getWrapper('control container');
  10993. if ($this->counter % 2) {
  10994. $body->class($this->getValue('control .odd'), TRUE);
  10995. }
  10996. $description = $control->getOption('description');
  10997. if ($description instanceof Html) {
  10998. $description = ' ' . $control->getOption('description');
  10999. } elseif (is_string($description)) {
  11000. $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
  11001. } else {
  11002. $description = '';
  11003. }
  11004. if ($control->isRequired()) {
  11005. $description = $this->getValue('control requiredsuffix') . $description;
  11006. }
  11007. if ($this->getValue('control errors')) {
  11008. $description .= $this->renderErrors($control);
  11009. }
  11010. if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
  11011. return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
  11012. } else {
  11013. return $body->setHtml((string) $control->getControl() . $description);
  11014. }
  11015. }
  11016. protected function getWrapper($name)
  11017. {
  11018. $data = $this->getValue($name);
  11019. return $data instanceof Html ? clone $data : Html::el($data);
  11020. }
  11021. protected function getValue($name)
  11022. {
  11023. $name = explode(' ', $name);
  11024. $data = & $this->wrappers[$name[0]][$name[1]];
  11025. return $data;
  11026. }
  11027. }
  11028. }
  11029. namespace Nette\Forms {
  11030. use Nette;
  11031. final class Rule extends Nette\Object
  11032. {
  11033. const CONDITION = 1;
  11034. const VALIDATOR = 2;
  11035. const FILTER = 3;
  11036. public $control;
  11037. public $operation;
  11038. public $arg;
  11039. public $type;
  11040. public $isNegative = FALSE;
  11041. public $message;
  11042. public $subRules;
  11043. }
  11044. final class Rules extends Nette\Object implements \IteratorAggregate
  11045. {
  11046. const VALIDATE_PREFIX = 'validate';
  11047. public static $defaultMessages = array(
  11048. Form::PROTECTION => 'Security token did not match. Possible CSRF attack.',
  11049. Form::EQUAL => 'Please enter %s.',
  11050. Form::FILLED => 'Please complete mandatory field.',
  11051. Form::MIN_LENGTH => 'Please enter a value of at least %d characters.',
  11052. Form::MAX_LENGTH => 'Please enter a value no longer than %d characters.',
  11053. Form::LENGTH => 'Please enter a value between %d and %d characters long.',
  11054. Form::EMAIL => 'Please enter a valid email address.',
  11055. Form::URL => 'Please enter a valid URL.',
  11056. Form::INTEGER => 'Please enter a numeric value.',
  11057. Form::FLOAT => 'Please enter a numeric value.',
  11058. Form::RANGE => 'Please enter a value between %d and %d.',
  11059. Form::MAX_FILE_SIZE => 'The size of the uploaded file can be up to %d bytes.',
  11060. Form::IMAGE => 'The uploaded file must be image in format JPEG, GIF or PNG.',
  11061. );
  11062. private $rules = array();
  11063. private $parent;
  11064. private $toggles = array();
  11065. private $control;
  11066. function __construct(IControl $control)
  11067. {
  11068. $this->control = $control;
  11069. }
  11070. function addRule($operation, $message = NULL, $arg = NULL)
  11071. {
  11072. $rule = new Rule;
  11073. $rule->control = $this->control;
  11074. $rule->operation = $operation;
  11075. $this->adjustOperation($rule);
  11076. $rule->arg = $arg;
  11077. $rule->type = Rule::VALIDATOR;
  11078. if ($message === NULL && is_string($rule->operation) && isset(self::$defaultMessages[$rule->operation])) {
  11079. $rule->message = self::$defaultMessages[$rule->operation];
  11080. } else {
  11081. $rule->message = $message;
  11082. }
  11083. $this->rules[] = $rule;
  11084. return $this;
  11085. }
  11086. function addCondition($operation, $arg = NULL)
  11087. {
  11088. return $this->addConditionOn($this->control, $operation, $arg);
  11089. }
  11090. function addConditionOn(IControl $control, $operation, $arg = NULL)
  11091. {
  11092. $rule = new Rule;
  11093. $rule->control = $control;
  11094. $rule->operation = $operation;
  11095. $this->adjustOperation($rule);
  11096. $rule->arg = $arg;
  11097. $rule->type = Rule::CONDITION;
  11098. $rule->subRules = new static($this->control);
  11099. $rule->subRules->parent = $this;
  11100. $this->rules[] = $rule;
  11101. return $rule->subRules;
  11102. }
  11103. function elseCondition()
  11104. {
  11105. $rule = clone end($this->parent->rules);
  11106. $rule->isNegative = !$rule->isNegative;
  11107. $rule->subRules = new static($this->parent->control);
  11108. $rule->subRules->parent = $this->parent;
  11109. $this->parent->rules[] = $rule;
  11110. return $rule->subRules;
  11111. }
  11112. function endCondition()
  11113. {
  11114. return $this->parent;
  11115. }
  11116. function toggle($id, $hide = TRUE)
  11117. {
  11118. $this->toggles[$id] = $hide;
  11119. return $this;
  11120. }
  11121. function validate($onlyCheck = FALSE)
  11122. {
  11123. foreach ($this->rules as $rule) {
  11124. if ($rule->control->isDisabled()) {
  11125. continue;
  11126. }
  11127. $success = ($rule->isNegative xor $this->getCallback($rule)->invoke($rule->control, $rule->arg));
  11128. if ($rule->type === Rule::CONDITION && $success) {
  11129. if (!$rule->subRules->validate($onlyCheck)) {
  11130. return FALSE;
  11131. }
  11132. } elseif ($rule->type === Rule::VALIDATOR && !$success) {
  11133. if (!$onlyCheck) {
  11134. $rule->control->addError(self::formatMessage($rule, TRUE));
  11135. }
  11136. return FALSE;
  11137. }
  11138. }
  11139. return TRUE;
  11140. }
  11141. final function getIterator()
  11142. {
  11143. return new \ArrayIterator($this->rules);
  11144. }
  11145. final function getToggles()
  11146. {
  11147. return $this->toggles;
  11148. }
  11149. private function adjustOperation($rule)
  11150. {
  11151. if (is_string($rule->operation) && ord($rule->operation[0]) > 127) {
  11152. $rule->isNegative = TRUE;
  11153. $rule->operation = ~$rule->operation;
  11154. }
  11155. if (!$this->getCallback($rule)->isCallable()) {
  11156. $operation = is_scalar($rule->operation) ? " '$rule->operation'" : '';
  11157. throw new Nette\InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'.");
  11158. }
  11159. }
  11160. private function getCallback($rule)
  11161. {
  11162. $op = $rule->operation;
  11163. if (is_string($op) && strncmp($op, ':', 1) === 0) {
  11164. return callback(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':'));
  11165. } else {
  11166. return callback($op);
  11167. }
  11168. }
  11169. static function formatMessage($rule, $withValue)
  11170. {
  11171. $message = $rule->message;
  11172. if (!isset($message)) {
  11173. $message = self::$defaultMessages[$rule->operation];
  11174. }
  11175. if ($translator = $rule->control->getForm()->getTranslator()) {
  11176. $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL);
  11177. }
  11178. $message = vsprintf(preg_replace('#%(name|label|value)#', '%$0', $message), (array) $rule->arg);
  11179. $message = str_replace('%name', $rule->control->getName(), $message);
  11180. $message = str_replace('%label', $rule->control->translate($rule->control->caption), $message);
  11181. if ($withValue && strpos($message, '%value') !== FALSE) {
  11182. $message = str_replace('%value', $rule->control->getValue(), $message);
  11183. }
  11184. return $message;
  11185. }
  11186. }
  11187. }
  11188. namespace Nette\Http {
  11189. use Nette;
  11190. class Context extends Nette\Object
  11191. {
  11192. private $request;
  11193. private $response;
  11194. function __construct(IRequest $request, IResponse $response)
  11195. {
  11196. $this->request = $request;
  11197. $this->response = $response;
  11198. }
  11199. function isModified($lastModified = NULL, $etag = NULL)
  11200. {
  11201. if ($lastModified) {
  11202. $this->response->setHeader('Last-Modified', $this->response->date($lastModified));
  11203. }
  11204. if ($etag) {
  11205. $this->response->setHeader('ETag', '"' . addslashes($etag) . '"');
  11206. }
  11207. $ifNoneMatch = $this->request->getHeader('If-None-Match');
  11208. if ($ifNoneMatch === '*') {
  11209. $match = TRUE;
  11210. } elseif ($ifNoneMatch !== NULL) {
  11211. $etag = $this->response->getHeader('ETag');
  11212. if ($etag == NULL || strpos(' ' . strtr($ifNoneMatch, ",\t", ' '), ' ' . $etag) === FALSE) {
  11213. return TRUE;
  11214. } else {
  11215. $match = TRUE;
  11216. }
  11217. }
  11218. $ifModifiedSince = $this->request->getHeader('If-Modified-Since');
  11219. if ($ifModifiedSince !== NULL) {
  11220. $lastModified = $this->response->getHeader('Last-Modified');
  11221. if ($lastModified != NULL && strtotime($lastModified) <= strtotime($ifModifiedSince)) {
  11222. $match = TRUE;
  11223. } else {
  11224. return TRUE;
  11225. }
  11226. }
  11227. if (empty($match)) {
  11228. return TRUE;
  11229. }
  11230. $this->response->setCode(IResponse::S304_NOT_MODIFIED);
  11231. return FALSE;
  11232. }
  11233. function getRequest()
  11234. {
  11235. return $this->request;
  11236. }
  11237. function getResponse()
  11238. {
  11239. return $this->response;
  11240. }
  11241. }
  11242. class FileUpload extends Nette\Object
  11243. {
  11244. private $name;
  11245. private $type;
  11246. private $size;
  11247. private $tmpName;
  11248. private $error;
  11249. function __construct($value)
  11250. {
  11251. foreach (array('name', 'type', 'size', 'tmp_name', 'error') as $key) {
  11252. if (!isset($value[$key]) || !is_scalar($value[$key])) {
  11253. $this->error = UPLOAD_ERR_NO_FILE;
  11254. return;
  11255. }
  11256. }
  11257. $this->name = $value['name'];
  11258. $this->size = $value['size'];
  11259. $this->tmpName = $value['tmp_name'];
  11260. $this->error = $value['error'];
  11261. }
  11262. function getName()
  11263. {
  11264. return $this->name;
  11265. }
  11266. function getContentType()
  11267. {
  11268. if ($this->isOk() && $this->type === NULL) {
  11269. $this->type = Nette\Utils\MimeTypeDetector::fromFile($this->tmpName);
  11270. }
  11271. return $this->type;
  11272. }
  11273. function getSize()
  11274. {
  11275. return $this->size;
  11276. }
  11277. function getTemporaryFile()
  11278. {
  11279. return $this->tmpName;
  11280. }
  11281. function __toString()
  11282. {
  11283. return $this->tmpName;
  11284. }
  11285. function getError()
  11286. {
  11287. return $this->error;
  11288. }
  11289. function isOk()
  11290. {
  11291. return $this->error === UPLOAD_ERR_OK;
  11292. }
  11293. function move($dest)
  11294. {
  11295. $dir = dirname($dest);
  11296. if (@mkdir($dir, 0755, TRUE)) {
  11297. chmod($dir, 0755);
  11298. }
  11299. $func = is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename';
  11300. if (!$func($this->tmpName, $dest)) {
  11301. throw new Nette\InvalidStateException("Unable to move uploaded file '$this->tmpName' to '$dest'.");
  11302. }
  11303. chmod($dest, 0644);
  11304. $this->tmpName = $dest;
  11305. return $this;
  11306. }
  11307. function isImage()
  11308. {
  11309. return in_array($this->getContentType(), array('image/gif', 'image/png', 'image/jpeg'), TRUE);
  11310. }
  11311. function toImage()
  11312. {
  11313. return Nette\Image::fromFile($this->tmpName);
  11314. }
  11315. function getImageSize()
  11316. {
  11317. return $this->isOk() ? @getimagesize($this->tmpName) : NULL;
  11318. }
  11319. function getContents()
  11320. {
  11321. return $this->isOk() ? file_get_contents($this->tmpName) : NULL;
  11322. }
  11323. }
  11324. class Request extends Nette\Object implements IRequest
  11325. {
  11326. private $method;
  11327. private $url;
  11328. private $query;
  11329. private $post;
  11330. private $files;
  11331. private $cookies;
  11332. private $headers;
  11333. private $remoteAddress;
  11334. private $remoteHost;
  11335. function __construct(UrlScript $url, $query = NULL, $post = NULL, $files = NULL, $cookies = NULL,
  11336. $headers = NULL, $method = NULL, $remoteAddress = NULL, $remoteHost = NULL)
  11337. {
  11338. $this->url = $url;
  11339. $this->url->freeze();
  11340. if ($query === NULL) {
  11341. parse_str($url->query, $this->query);
  11342. } else {
  11343. $this->query = (array) $query;
  11344. }
  11345. $this->post = (array) $post;
  11346. $this->files = (array) $files;
  11347. $this->cookies = (array) $cookies;
  11348. $this->headers = (array) $headers;
  11349. $this->method = $method;
  11350. $this->remoteAddress = $remoteAddress;
  11351. $this->remoteHost = $remoteHost;
  11352. }
  11353. final function getUrl()
  11354. {
  11355. return $this->url;
  11356. }
  11357. function getUri()
  11358. {
  11359. trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getUrl() instead.', E_USER_WARNING);
  11360. return $this->getUrl();
  11361. }
  11362. final function getQuery($key = NULL, $default = NULL)
  11363. {
  11364. if (func_num_args() === 0) {
  11365. return $this->query;
  11366. } elseif (isset($this->query[$key])) {
  11367. return $this->query[$key];
  11368. } else {
  11369. return $default;
  11370. }
  11371. }
  11372. final function getPost($key = NULL, $default = NULL)
  11373. {
  11374. if (func_num_args() === 0) {
  11375. return $this->post;
  11376. } elseif (isset($this->post[$key])) {
  11377. return $this->post[$key];
  11378. } else {
  11379. return $default;
  11380. }
  11381. }
  11382. final function getFile($key)
  11383. {
  11384. $args = func_get_args();
  11385. return Nette\Utils\Arrays::get($this->files, $args, NULL);
  11386. }
  11387. final function getFiles()
  11388. {
  11389. return $this->files;
  11390. }
  11391. final function getCookie($key, $default = NULL)
  11392. {
  11393. if (func_num_args() === 0) {
  11394. return $this->cookies;
  11395. } elseif (isset($this->cookies[$key])) {
  11396. return $this->cookies[$key];
  11397. } else {
  11398. return $default;
  11399. }
  11400. }
  11401. final function getCookies()
  11402. {
  11403. return $this->cookies;
  11404. }
  11405. function getMethod()
  11406. {
  11407. return $this->method;
  11408. }
  11409. function isMethod($method)
  11410. {
  11411. return strcasecmp($this->method, $method) === 0;
  11412. }
  11413. function isPost()
  11414. {
  11415. return $this->isMethod('POST');
  11416. }
  11417. final function getHeader($header, $default = NULL)
  11418. {
  11419. $header = strtolower($header);
  11420. if (isset($this->headers[$header])) {
  11421. return $this->headers[$header];
  11422. } else {
  11423. return $default;
  11424. }
  11425. }
  11426. function getHeaders()
  11427. {
  11428. return $this->headers;
  11429. }
  11430. final function getReferer()
  11431. {
  11432. return isset($this->headers['referer']) ? new Url($this->headers['referer']) : NULL;
  11433. }
  11434. function isSecured()
  11435. {
  11436. return $this->url->scheme === 'https';
  11437. }
  11438. function isAjax()
  11439. {
  11440. return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
  11441. }
  11442. function getRemoteAddress()
  11443. {
  11444. return $this->remoteAddress;
  11445. }
  11446. function getRemoteHost()
  11447. {
  11448. if (!$this->remoteHost) {
  11449. $this->remoteHost = $this->remoteAddress ? getHostByAddr($this->remoteAddress) : NULL;
  11450. }
  11451. return $this->remoteHost;
  11452. }
  11453. function detectLanguage(array $langs)
  11454. {
  11455. $header = $this->getHeader('Accept-Language');
  11456. if (!$header) {
  11457. return NULL;
  11458. }
  11459. $s = strtolower($header);
  11460. $s = strtr($s, '_', '-');
  11461. rsort($langs);
  11462. preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches);
  11463. if (!$matches[0]) {
  11464. return NULL;
  11465. }
  11466. $max = 0;
  11467. $lang = NULL;
  11468. foreach ($matches[1] as $key => $value) {
  11469. $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key];
  11470. if ($q > $max) {
  11471. $max = $q; $lang = $value;
  11472. }
  11473. }
  11474. return $lang;
  11475. }
  11476. }
  11477. use Nette\Utils\Strings;
  11478. class RequestFactory extends Nette\Object
  11479. {
  11480. const NONCHARS = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
  11481. public $urlFilters = array(
  11482. 'path' => array('#/{2,}#' => '/'),
  11483. 'url' => array(),
  11484. );
  11485. private $encoding;
  11486. function setEncoding($encoding)
  11487. {
  11488. $this->encoding = $encoding;
  11489. return $this;
  11490. }
  11491. function createHttpRequest()
  11492. {
  11493. $url = new UrlScript;
  11494. $url->scheme = isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http';
  11495. $url->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
  11496. $url->password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
  11497. if (isset($_SERVER['HTTP_HOST'])) {
  11498. $pair = explode(':', $_SERVER['HTTP_HOST']);
  11499. } elseif (isset($_SERVER['SERVER_NAME'])) {
  11500. $pair = explode(':', $_SERVER['SERVER_NAME']);
  11501. } else {
  11502. $pair = array('');
  11503. }
  11504. $url->host = preg_match('#^[-._a-z0-9]+$#', $pair[0]) ? $pair[0] : '';
  11505. if (isset($pair[1])) {
  11506. $url->port = (int) $pair[1];
  11507. } elseif (isset($_SERVER['SERVER_PORT'])) {
  11508. $url->port = (int) $_SERVER['SERVER_PORT'];
  11509. }
  11510. if (isset($_SERVER['REQUEST_URI'])) {
  11511. $requestUrl = $_SERVER['REQUEST_URI'];
  11512. } elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
  11513. $requestUrl = $_SERVER['ORIG_PATH_INFO'];
  11514. if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') {
  11515. $requestUrl .= '?' . $_SERVER['QUERY_STRING'];
  11516. }
  11517. } else {
  11518. $requestUrl = '';
  11519. }
  11520. $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
  11521. $tmp = explode('?', $requestUrl, 2);
  11522. $url->path = Strings::replace($tmp[0], $this->urlFilters['path']);
  11523. $url->query = isset($tmp[1]) ? $tmp[1] : '';
  11524. $url->canonicalize();
  11525. $url->path = Strings::fixEncoding($url->path);
  11526. if (isset($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME'])
  11527. && strncmp($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])) === 0
  11528. ) {
  11529. $script = '/' . ltrim(strtr(substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])), '\\', '/'), '/');
  11530. } elseif (isset($_SERVER['SCRIPT_NAME'])) {
  11531. $script = $_SERVER['SCRIPT_NAME'];
  11532. } else {
  11533. $script = '/';
  11534. }
  11535. if (strncasecmp($url->path . '/', $script . '/', strlen($script) + 1) === 0) {
  11536. $url->scriptPath = substr($url->path, 0, strlen($script));
  11537. } elseif (strncasecmp($url->path, $script, strrpos($script, '/') + 1) === 0) {
  11538. $url->scriptPath = substr($url->path, 0, strrpos($script, '/') + 1);
  11539. } else {
  11540. $url->scriptPath = '/';
  11541. }
  11542. $useFilter = (!in_array(ini_get('filter.default'), array('', 'unsafe_raw')) || ini_get('filter.default_flags'));
  11543. parse_str($url->query, $query);
  11544. if (!$query) {
  11545. $query = $useFilter ? filter_input_array(INPUT_GET, FILTER_UNSAFE_RAW) : (empty($_GET) ? array() : $_GET);
  11546. }
  11547. $post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST);
  11548. $cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE);
  11549. $gpc = (bool) get_magic_quotes_gpc();
  11550. $old = error_reporting(error_reporting() ^ E_NOTICE);
  11551. if ($gpc || $this->encoding) {
  11552. $utf = strcasecmp($this->encoding, 'UTF-8') === 0;
  11553. $list = array(& $query, & $post, & $cookies);
  11554. while (list($key, $val) = each($list)) {
  11555. foreach ($val as $k => $v) {
  11556. unset($list[$key][$k]);
  11557. if ($gpc) {
  11558. $k = stripslashes($k);
  11559. }
  11560. if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) {
  11561. } elseif (is_array($v)) {
  11562. $list[$key][$k] = $v;
  11563. $list[] = & $list[$key][$k];
  11564. } else {
  11565. if ($gpc && !$useFilter) {
  11566. $v = stripSlashes($v);
  11567. }
  11568. if ($this->encoding) {
  11569. if ($utf) {
  11570. $v = Strings::fixEncoding($v);
  11571. } else {
  11572. if (!Strings::checkEncoding($v)) {
  11573. $v = iconv($this->encoding, 'UTF-8//IGNORE', $v);
  11574. }
  11575. $v = html_entity_decode($v, ENT_QUOTES, 'UTF-8');
  11576. }
  11577. $v = preg_replace(self::NONCHARS, '', $v);
  11578. }
  11579. $list[$key][$k] = $v;
  11580. }
  11581. }
  11582. }
  11583. unset($list, $key, $val, $k, $v);
  11584. }
  11585. $files = array();
  11586. $list = array();
  11587. if (!empty($_FILES)) {
  11588. foreach ($_FILES as $k => $v) {
  11589. if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) {
  11590. continue;
  11591. }
  11592. $v['@'] = & $files[$k];
  11593. $list[] = $v;
  11594. }
  11595. }
  11596. while (list(, $v) = each($list)) {
  11597. if (!isset($v['name'])) {
  11598. continue;
  11599. } elseif (!is_array($v['name'])) {
  11600. if ($gpc) {
  11601. $v['name'] = stripSlashes($v['name']);
  11602. }
  11603. if ($this->encoding) {
  11604. $v['name'] = preg_replace(self::NONCHARS, '', Strings::fixEncoding($v['name']));
  11605. }
  11606. $v['@'] = new FileUpload($v);
  11607. continue;
  11608. }
  11609. foreach ($v['name'] as $k => $foo) {
  11610. if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) {
  11611. continue;
  11612. }
  11613. $list[] = array(
  11614. 'name' => $v['name'][$k],
  11615. 'type' => $v['type'][$k],
  11616. 'size' => $v['size'][$k],
  11617. 'tmp_name' => $v['tmp_name'][$k],
  11618. 'error' => $v['error'][$k],
  11619. '@' => & $v['@'][$k],
  11620. );
  11621. }
  11622. }
  11623. error_reporting($old);
  11624. if (function_exists('apache_request_headers')) {
  11625. $headers = array_change_key_case(apache_request_headers(), CASE_LOWER);
  11626. } else {
  11627. $headers = array();
  11628. foreach ($_SERVER as $k => $v) {
  11629. if (strncmp($k, 'HTTP_', 5) == 0) {
  11630. $k = substr($k, 5);
  11631. } elseif (strncmp($k, 'CONTENT_', 8)) {
  11632. continue;
  11633. }
  11634. $headers[ strtr(strtolower($k), '_', '-') ] = $v;
  11635. }
  11636. }
  11637. return new Request($url, $query, $post, $files, $cookies, $headers,
  11638. isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL,
  11639. isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL,
  11640. isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL
  11641. );
  11642. }
  11643. }
  11644. final class Response extends Nette\Object implements IResponse
  11645. {
  11646. private static $fixIE = TRUE;
  11647. public $cookieDomain = '';
  11648. public $cookiePath = '/';
  11649. public $cookieSecure = FALSE;
  11650. public $cookieHttpOnly = TRUE;
  11651. private $code = self::S200_OK;
  11652. function setCode($code)
  11653. {
  11654. $code = (int) $code;
  11655. static $allowed = array(
  11656. 200=>1, 201=>1, 202=>1, 203=>1, 204=>1, 205=>1, 206=>1,
  11657. 300=>1, 301=>1, 302=>1, 303=>1, 304=>1, 307=>1,
  11658. 400=>1, 401=>1, 403=>1, 404=>1, 405=>1, 406=>1, 408=>1, 410=>1, 412=>1, 415=>1, 416=>1,
  11659. 500=>1, 501=>1, 503=>1, 505=>1
  11660. );
  11661. if (!isset($allowed[$code])) {
  11662. throw new Nette\InvalidArgumentException("Bad HTTP response '$code'.");
  11663. } elseif (headers_sent($file, $line)) {
  11664. throw new Nette\InvalidStateException("Cannot set HTTP code after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  11665. } else {
  11666. $this->code = $code;
  11667. $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
  11668. header($protocol . ' ' . $code, TRUE, $code);
  11669. }
  11670. return $this;
  11671. }
  11672. function getCode()
  11673. {
  11674. return $this->code;
  11675. }
  11676. function setHeader($name, $value)
  11677. {
  11678. if (headers_sent($file, $line)) {
  11679. throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  11680. }
  11681. if ($value === NULL && function_exists('header_remove')) {
  11682. header_remove($name);
  11683. } else {
  11684. header($name . ': ' . $value, TRUE, $this->code);
  11685. }
  11686. return $this;
  11687. }
  11688. function addHeader($name, $value)
  11689. {
  11690. if (headers_sent($file, $line)) {
  11691. throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  11692. }
  11693. header($name . ': ' . $value, FALSE, $this->code);
  11694. }
  11695. function setContentType($type, $charset = NULL)
  11696. {
  11697. $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
  11698. return $this;
  11699. }
  11700. function redirect($url, $code = self::S302_FOUND)
  11701. {
  11702. if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('#^Microsoft-IIS/[1-5]#', $_SERVER['SERVER_SOFTWARE'])
  11703. && $this->getHeader('Set-Cookie') !== NULL
  11704. ) {
  11705. $this->setHeader('Refresh', "0;url=$url");
  11706. return;
  11707. }
  11708. $this->setCode($code);
  11709. $this->setHeader('Location', $url);
  11710. echo "<h1>Redirect</h1>\n\n<p><a href=\"" . htmlSpecialChars($url) . "\">Please click here to continue</a>.</p>";
  11711. }
  11712. function setExpiration($time)
  11713. {
  11714. if (!$time) {
  11715. $this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate');
  11716. $this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT');
  11717. return $this;
  11718. }
  11719. $time = Nette\DateTime::from($time);
  11720. $this->setHeader('Cache-Control', 'max-age=' . ($time->format('U') - time()));
  11721. $this->setHeader('Expires', self::date($time));
  11722. return $this;
  11723. }
  11724. function isSent()
  11725. {
  11726. return headers_sent();
  11727. }
  11728. function getHeader($header, $default = NULL)
  11729. {
  11730. $header .= ':';
  11731. $len = strlen($header);
  11732. foreach (headers_list() as $item) {
  11733. if (strncasecmp($item, $header, $len) === 0) {
  11734. return ltrim(substr($item, $len));
  11735. }
  11736. }
  11737. return $default;
  11738. }
  11739. function getHeaders()
  11740. {
  11741. $headers = array();
  11742. foreach (headers_list() as $header) {
  11743. $a = strpos($header, ':');
  11744. $headers[substr($header, 0, $a)] = (string) substr($header, $a + 2);
  11745. }
  11746. return $headers;
  11747. }
  11748. static function date($time = NULL)
  11749. {
  11750. $time = Nette\DateTime::from($time);
  11751. $time->setTimezone(new \DateTimeZone('GMT'));
  11752. return $time->format('D, d M Y H:i:s \G\M\T');
  11753. }
  11754. function __destruct()
  11755. {
  11756. if (self::$fixIE && isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE
  11757. && in_array($this->code, array(400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505), TRUE)
  11758. && $this->getHeader('Content-Type', 'text/html') === 'text/html'
  11759. ) {
  11760. echo Nette\Utils\Strings::random(2e3, " \t\r\n");
  11761. self::$fixIE = FALSE;
  11762. }
  11763. }
  11764. function setCookie($name, $value, $time, $path = NULL, $domain = NULL, $secure = NULL, $httpOnly = NULL)
  11765. {
  11766. if (headers_sent($file, $line)) {
  11767. throw new Nette\InvalidStateException("Cannot set cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  11768. }
  11769. setcookie(
  11770. $name,
  11771. $value,
  11772. $time ? Nette\DateTime::from($time)->format('U') : 0,
  11773. $path === NULL ? $this->cookiePath : (string) $path,
  11774. $domain === NULL ? $this->cookieDomain : (string) $domain,
  11775. $secure === NULL ? $this->cookieSecure : (bool) $secure,
  11776. $httpOnly === NULL ? $this->cookieHttpOnly : (bool) $httpOnly
  11777. );
  11778. return $this;
  11779. }
  11780. function deleteCookie($name, $path = NULL, $domain = NULL, $secure = NULL)
  11781. {
  11782. if (headers_sent($file, $line)) {
  11783. throw new Nette\InvalidStateException("Cannot delete cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  11784. }
  11785. setcookie(
  11786. $name,
  11787. FALSE,
  11788. 254400000,
  11789. $path === NULL ? $this->cookiePath : (string) $path,
  11790. $domain === NULL ? $this->cookieDomain : (string) $domain,
  11791. $secure === NULL ? $this->cookieSecure : (bool) $secure,
  11792. TRUE
  11793. );
  11794. }
  11795. }
  11796. class Session extends Nette\Object
  11797. {
  11798. const DEFAULT_FILE_LIFETIME = 10800;
  11799. private $regenerationNeeded;
  11800. private static $started;
  11801. private $options = array(
  11802. 'referer_check' => '',
  11803. 'use_cookies' => 1,
  11804. 'use_only_cookies' => 1,
  11805. 'use_trans_sid' => 0,
  11806. 'cookie_lifetime' => 0,
  11807. 'cookie_path' => '/',
  11808. 'cookie_domain' => '',
  11809. 'cookie_secure' => FALSE,
  11810. 'cookie_httponly' => TRUE,
  11811. 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME,
  11812. 'cache_limiter' => NULL,
  11813. 'cache_expire' => NULL,
  11814. 'hash_function' => NULL,
  11815. 'hash_bits_per_character' => NULL,
  11816. );
  11817. private $request;
  11818. private $response;
  11819. function __construct(IRequest $request, IResponse $response)
  11820. {
  11821. $this->request = $request;
  11822. $this->response = $response;
  11823. }
  11824. function start()
  11825. {
  11826. if (self::$started) {
  11827. return;
  11828. } elseif (self::$started === NULL && defined('SID')) {
  11829. throw new Nette\InvalidStateException('A session had already been started by session.auto_start or session_start().');
  11830. }
  11831. $this->configure($this->options);
  11832. Nette\Diagnostics\Debugger::tryError();
  11833. session_start();
  11834. if (Nette\Diagnostics\Debugger::catchError($e)) {
  11835. @session_write_close();
  11836. throw new Nette\InvalidStateException('session_start(): ' . $e->getMessage(), 0, $e);
  11837. }
  11838. self::$started = TRUE;
  11839. if ($this->regenerationNeeded) {
  11840. session_regenerate_id(TRUE);
  11841. $this->regenerationNeeded = FALSE;
  11842. }
  11843. unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']);
  11844. $nf = & $_SESSION['__NF'];
  11845. if (empty($nf)) {
  11846. $nf = array('C' => 0);
  11847. } else {
  11848. $nf['C']++;
  11849. }
  11850. $browserKey = $this->request->getCookie('nette-browser');
  11851. if (!$browserKey) {
  11852. $browserKey = Nette\Utils\Strings::random();
  11853. }
  11854. $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey;
  11855. $nf['B'] = $browserKey;
  11856. $this->sendCookie();
  11857. if (isset($nf['META'])) {
  11858. $now = time();
  11859. foreach ($nf['META'] as $section => $metadata) {
  11860. if (is_array($metadata)) {
  11861. foreach ($metadata as $variable => $value) {
  11862. if ((!empty($value['B']) && $browserClosed) || (!empty($value['T']) && $now > $value['T'])
  11863. || ($variable !== '' && is_object($nf['DATA'][$section][$variable]) && (isset($value['V']) ? $value['V'] : NULL)
  11864. !== Nette\Reflection\ClassType::from($nf['DATA'][$section][$variable])->getAnnotation('serializationVersion'))
  11865. ) {
  11866. if ($variable === '') {
  11867. unset($nf['META'][$section], $nf['DATA'][$section]);
  11868. continue 2;
  11869. }
  11870. unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]);
  11871. }
  11872. }
  11873. }
  11874. }
  11875. }
  11876. register_shutdown_function(array($this, 'clean'));
  11877. }
  11878. function isStarted()
  11879. {
  11880. return (bool) self::$started;
  11881. }
  11882. function close()
  11883. {
  11884. if (self::$started) {
  11885. $this->clean();
  11886. session_write_close();
  11887. self::$started = FALSE;
  11888. }
  11889. }
  11890. function destroy()
  11891. {
  11892. if (!self::$started) {
  11893. throw new Nette\InvalidStateException('Session is not started.');
  11894. }
  11895. session_destroy();
  11896. $_SESSION = NULL;
  11897. self::$started = FALSE;
  11898. if (!$this->response->isSent()) {
  11899. $params = session_get_cookie_params();
  11900. $this->response->deleteCookie(session_name(), $params['path'], $params['domain'], $params['secure']);
  11901. }
  11902. }
  11903. function exists()
  11904. {
  11905. return self::$started || $this->request->getCookie(session_name()) !== NULL;
  11906. }
  11907. function regenerateId()
  11908. {
  11909. if (self::$started) {
  11910. if (headers_sent($file, $line)) {
  11911. throw new Nette\InvalidStateException("Cannot regenerate session ID after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  11912. }
  11913. session_regenerate_id(TRUE);
  11914. } else {
  11915. $this->regenerationNeeded = TRUE;
  11916. }
  11917. }
  11918. function getId()
  11919. {
  11920. return session_id();
  11921. }
  11922. function setName($name)
  11923. {
  11924. if (!is_string($name) || !preg_match('#[^0-9.][^.]*$#A', $name)) {
  11925. throw new Nette\InvalidArgumentException('Session name must be a string and cannot contain dot.');
  11926. }
  11927. session_name($name);
  11928. return $this->setOptions(array(
  11929. 'name' => $name,
  11930. ));
  11931. }
  11932. function getName()
  11933. {
  11934. return session_name();
  11935. }
  11936. function getSection($section, $class = 'Nette\Http\SessionSection')
  11937. {
  11938. return new $class($this, $section);
  11939. }
  11940. function getNamespace($section)
  11941. {
  11942. trigger_error(__METHOD__ . '() is deprecated; use getSection() instead.', E_USER_WARNING);
  11943. return $this->getSection($section);
  11944. }
  11945. function hasSection($section)
  11946. {
  11947. if ($this->exists() && !self::$started) {
  11948. $this->start();
  11949. }
  11950. return !empty($_SESSION['__NF']['DATA'][$section]);
  11951. }
  11952. function getIterator()
  11953. {
  11954. if ($this->exists() && !self::$started) {
  11955. $this->start();
  11956. }
  11957. if (isset($_SESSION['__NF']['DATA'])) {
  11958. return new \ArrayIterator(array_keys($_SESSION['__NF']['DATA']));
  11959. } else {
  11960. return new \ArrayIterator;
  11961. }
  11962. }
  11963. function clean()
  11964. {
  11965. if (!self::$started || empty($_SESSION)) {
  11966. return;
  11967. }
  11968. $nf = & $_SESSION['__NF'];
  11969. if (isset($nf['META']) && is_array($nf['META'])) {
  11970. foreach ($nf['META'] as $name => $foo) {
  11971. if (empty($nf['META'][$name])) {
  11972. unset($nf['META'][$name]);
  11973. }
  11974. }
  11975. }
  11976. if (empty($nf['META'])) {
  11977. unset($nf['META']);
  11978. }
  11979. if (empty($nf['DATA'])) {
  11980. unset($nf['DATA']);
  11981. }
  11982. if (empty($_SESSION)) {
  11983. }
  11984. }
  11985. function setOptions(array $options)
  11986. {
  11987. if (self::$started) {
  11988. $this->configure($options);
  11989. }
  11990. $this->options = $options + $this->options;
  11991. if (!empty($options['auto_start'])) {
  11992. $this->start();
  11993. }
  11994. return $this;
  11995. }
  11996. function getOptions()
  11997. {
  11998. return $this->options;
  11999. }
  12000. private function configure(array $config)
  12001. {
  12002. $special = array('cache_expire' => 1, 'cache_limiter' => 1, 'save_path' => 1, 'name' => 1);
  12003. foreach ($config as $key => $value) {
  12004. if (!strncmp($key, 'session.', 8)) {
  12005. $key = substr($key, 8);
  12006. }
  12007. if ($value === NULL) {
  12008. continue;
  12009. } elseif (isset($special[$key])) {
  12010. if (self::$started) {
  12011. throw new Nette\InvalidStateException("Unable to set '$key' when session has been started.");
  12012. }
  12013. $key = "session_$key";
  12014. $key($value);
  12015. } elseif (strncmp($key, 'cookie_', 7) === 0) {
  12016. if (!isset($cookie)) {
  12017. $cookie = session_get_cookie_params();
  12018. }
  12019. $cookie[substr($key, 7)] = $value;
  12020. } elseif (!function_exists('ini_set')) {
  12021. if (ini_get($key) != $value && !Nette\Framework::$iAmUsingBadHost) {
  12022. throw new Nette\NotSupportedException('Required function ini_set() is disabled.');
  12023. }
  12024. } else {
  12025. if (self::$started) {
  12026. throw new Nette\InvalidStateException("Unable to set '$key' when session has been started.");
  12027. }
  12028. ini_set("session.$key", $value);
  12029. }
  12030. }
  12031. if (isset($cookie)) {
  12032. session_set_cookie_params(
  12033. $cookie['lifetime'], $cookie['path'], $cookie['domain'],
  12034. $cookie['secure'], $cookie['httponly']
  12035. );
  12036. if (self::$started) {
  12037. $this->sendCookie();
  12038. }
  12039. }
  12040. }
  12041. function setExpiration($time)
  12042. {
  12043. if (empty($time)) {
  12044. return $this->setOptions(array(
  12045. 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME,
  12046. 'cookie_lifetime' => 0,
  12047. ));
  12048. } else {
  12049. $time = Nette\DateTime::from($time)->format('U') - time();
  12050. return $this->setOptions(array(
  12051. 'gc_maxlifetime' => $time,
  12052. 'cookie_lifetime' => $time,
  12053. ));
  12054. }
  12055. }
  12056. function setCookieParams($path, $domain = NULL, $secure = NULL)
  12057. {
  12058. return $this->setOptions(array(
  12059. 'cookie_path' => $path,
  12060. 'cookie_domain' => $domain,
  12061. 'cookie_secure' => $secure
  12062. ));
  12063. }
  12064. function getCookieParams()
  12065. {
  12066. return session_get_cookie_params();
  12067. }
  12068. function setSavePath($path)
  12069. {
  12070. return $this->setOptions(array(
  12071. 'save_path' => $path,
  12072. ));
  12073. }
  12074. function setStorage(ISessionStorage $storage)
  12075. {
  12076. if (self::$started) {
  12077. throw new Nette\InvalidStateException("Unable to set storage when session has been started.");
  12078. }
  12079. session_set_save_handler(
  12080. array($storage, 'open'), array($storage, 'close'), array($storage, 'read'),
  12081. array($storage, 'write'), array($storage, 'remove'), array($storage, 'clean')
  12082. );
  12083. }
  12084. private function sendCookie()
  12085. {
  12086. $cookie = $this->getCookieParams();
  12087. $this->response->setCookie(
  12088. session_name(), session_id(),
  12089. $cookie['lifetime'] ? $cookie['lifetime'] + time() : 0,
  12090. $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly']
  12091. )->setCookie(
  12092. 'nette-browser', $_SESSION['__NF']['B'],
  12093. Response::BROWSER, $cookie['path'], $cookie['domain']
  12094. );
  12095. }
  12096. }
  12097. final class SessionSection extends Nette\Object implements \IteratorAggregate, \ArrayAccess
  12098. {
  12099. private $session;
  12100. private $name;
  12101. private $data;
  12102. private $meta = FALSE;
  12103. public $warnOnUndefined = FALSE;
  12104. function __construct(Session $session, $name)
  12105. {
  12106. if (!is_string($name)) {
  12107. throw new Nette\InvalidArgumentException("Session namespace must be a string, " . gettype($name) ." given.");
  12108. }
  12109. $this->session = $session;
  12110. $this->name = $name;
  12111. }
  12112. private function start()
  12113. {
  12114. if ($this->meta === FALSE) {
  12115. $this->session->start();
  12116. $this->data = & $_SESSION['__NF']['DATA'][$this->name];
  12117. $this->meta = & $_SESSION['__NF']['META'][$this->name];
  12118. }
  12119. }
  12120. function getIterator()
  12121. {
  12122. $this->start();
  12123. if (isset($this->data)) {
  12124. return new \ArrayIterator($this->data);
  12125. } else {
  12126. return new \ArrayIterator;
  12127. }
  12128. }
  12129. function __set($name, $value)
  12130. {
  12131. $this->start();
  12132. $this->data[$name] = $value;
  12133. if (is_object($value)) {
  12134. $this->meta[$name]['V'] = Nette\Reflection\ClassType::from($value)->getAnnotation('serializationVersion');
  12135. }
  12136. }
  12137. function &__get($name)
  12138. {
  12139. $this->start();
  12140. if ($this->warnOnUndefined && !array_key_exists($name, $this->data)) {
  12141. trigger_error("The variable '$name' does not exist in session section", E_USER_NOTICE);
  12142. }
  12143. return $this->data[$name];
  12144. }
  12145. function __isset($name)
  12146. {
  12147. if ($this->session->exists()) {
  12148. $this->start();
  12149. }
  12150. return isset($this->data[$name]);
  12151. }
  12152. function __unset($name)
  12153. {
  12154. $this->start();
  12155. unset($this->data[$name], $this->meta[$name]);
  12156. }
  12157. function offsetSet($name, $value)
  12158. {
  12159. $this->__set($name, $value);
  12160. }
  12161. function offsetGet($name)
  12162. {
  12163. return $this->__get($name);
  12164. }
  12165. function offsetExists($name)
  12166. {
  12167. return $this->__isset($name);
  12168. }
  12169. function offsetUnset($name)
  12170. {
  12171. $this->__unset($name);
  12172. }
  12173. function setExpiration($time, $variables = NULL)
  12174. {
  12175. $this->start();
  12176. if (empty($time)) {
  12177. $time = NULL;
  12178. $whenBrowserIsClosed = TRUE;
  12179. } else {
  12180. $time = Nette\DateTime::from($time)->format('U');
  12181. $max = ini_get('session.gc_maxlifetime');
  12182. if ($time - time() > $max + 3) {
  12183. trigger_error("The expiration time is greater than the session expiration $max seconds", E_USER_NOTICE);
  12184. }
  12185. $whenBrowserIsClosed = FALSE;
  12186. }
  12187. if ($variables === NULL) {
  12188. $this->meta['']['T'] = $time;
  12189. $this->meta['']['B'] = $whenBrowserIsClosed;
  12190. } elseif (is_array($variables)) {
  12191. foreach ($variables as $variable) {
  12192. $this->meta[$variable]['T'] = $time;
  12193. $this->meta[$variable]['B'] = $whenBrowserIsClosed;
  12194. }
  12195. } else {
  12196. $this->meta[$variables]['T'] = $time;
  12197. $this->meta[$variables]['B'] = $whenBrowserIsClosed;
  12198. }
  12199. return $this;
  12200. }
  12201. function removeExpiration($variables = NULL)
  12202. {
  12203. $this->start();
  12204. if ($variables === NULL) {
  12205. unset($this->meta['']['T'], $this->meta['']['B']);
  12206. } elseif (is_array($variables)) {
  12207. foreach ($variables as $variable) {
  12208. unset($this->meta[$variable]['T'], $this->meta[$variable]['B']);
  12209. }
  12210. } else {
  12211. unset($this->meta[$variables]['T'], $this->meta[$variable]['B']);
  12212. }
  12213. }
  12214. function remove()
  12215. {
  12216. $this->start();
  12217. $this->data = NULL;
  12218. $this->meta = NULL;
  12219. }
  12220. }
  12221. class Url extends Nette\FreezableObject
  12222. {
  12223. public static $defaultPorts = array(
  12224. 'http' => 80,
  12225. 'https' => 443,
  12226. 'ftp' => 21,
  12227. 'news' => 119,
  12228. 'nntp' => 119,
  12229. );
  12230. private $scheme = '';
  12231. private $user = '';
  12232. private $pass = '';
  12233. private $host = '';
  12234. private $port = NULL;
  12235. private $path = '';
  12236. private $query = '';
  12237. private $fragment = '';
  12238. function __construct($url = NULL)
  12239. {
  12240. if (is_string($url)) {
  12241. $parts = @parse_url($url);
  12242. if ($parts === FALSE) {
  12243. throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'.");
  12244. }
  12245. foreach ($parts as $key => $val) {
  12246. $this->$key = $val;
  12247. }
  12248. if (!$this->port && isset(self::$defaultPorts[$this->scheme])) {
  12249. $this->port = self::$defaultPorts[$this->scheme];
  12250. }
  12251. if ($this->path === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
  12252. $this->path = '/';
  12253. }
  12254. } elseif ($url instanceof self) {
  12255. foreach ($this as $key => $val) {
  12256. $this->$key = $url->$key;
  12257. }
  12258. }
  12259. }
  12260. function setScheme($value)
  12261. {
  12262. $this->updating();
  12263. $this->scheme = (string) $value;
  12264. return $this;
  12265. }
  12266. function getScheme()
  12267. {
  12268. return $this->scheme;
  12269. }
  12270. function setUser($value)
  12271. {
  12272. $this->updating();
  12273. $this->user = (string) $value;
  12274. return $this;
  12275. }
  12276. function getUser()
  12277. {
  12278. return $this->user;
  12279. }
  12280. function setPassword($value)
  12281. {
  12282. $this->updating();
  12283. $this->pass = (string) $value;
  12284. return $this;
  12285. }
  12286. function getPassword()
  12287. {
  12288. return $this->pass;
  12289. }
  12290. function setHost($value)
  12291. {
  12292. $this->updating();
  12293. $this->host = (string) $value;
  12294. return $this;
  12295. }
  12296. function getHost()
  12297. {
  12298. return $this->host;
  12299. }
  12300. function setPort($value)
  12301. {
  12302. $this->updating();
  12303. $this->port = (int) $value;
  12304. return $this;
  12305. }
  12306. function getPort()
  12307. {
  12308. return $this->port;
  12309. }
  12310. function setPath($value)
  12311. {
  12312. $this->updating();
  12313. $this->path = (string) $value;
  12314. return $this;
  12315. }
  12316. function getPath()
  12317. {
  12318. return $this->path;
  12319. }
  12320. function setQuery($value)
  12321. {
  12322. $this->updating();
  12323. $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
  12324. return $this;
  12325. }
  12326. function appendQuery($value)
  12327. {
  12328. $this->updating();
  12329. $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
  12330. $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value;
  12331. }
  12332. function getQuery()
  12333. {
  12334. return $this->query;
  12335. }
  12336. function setFragment($value)
  12337. {
  12338. $this->updating();
  12339. $this->fragment = (string) $value;
  12340. return $this;
  12341. }
  12342. function getFragment()
  12343. {
  12344. return $this->fragment;
  12345. }
  12346. function getAbsoluteUrl()
  12347. {
  12348. return $this->scheme . '://' . $this->getAuthority() . $this->path
  12349. . ($this->query === '' ? '' : '?' . $this->query)
  12350. . ($this->fragment === '' ? '' : '#' . $this->fragment);
  12351. }
  12352. function getAuthority()
  12353. {
  12354. $authority = $this->host;
  12355. if ($this->port && isset(self::$defaultPorts[$this->scheme]) && $this->port !== self::$defaultPorts[$this->scheme]) {
  12356. $authority .= ':' . $this->port;
  12357. }
  12358. if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') {
  12359. $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority;
  12360. }
  12361. return $authority;
  12362. }
  12363. function getHostUrl()
  12364. {
  12365. return $this->scheme . '://' . $this->getAuthority();
  12366. }
  12367. function getBasePath()
  12368. {
  12369. $pos = strrpos($this->path, '/');
  12370. return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1);
  12371. }
  12372. function getBaseUrl()
  12373. {
  12374. return $this->scheme . '://' . $this->getAuthority() . $this->getBasePath();
  12375. }
  12376. function getRelativeUrl()
  12377. {
  12378. return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl()));
  12379. }
  12380. function isEqual($url)
  12381. {
  12382. $part = self::unescape(strtok($url, '?#'), '%/');
  12383. if (strncmp($part, '//', 2) === 0) {
  12384. if ($part !== '//' . $this->getAuthority() . $this->path) {
  12385. return FALSE;
  12386. }
  12387. } elseif (strncmp($part, '/', 1) === 0) {
  12388. if ($part !== $this->path) {
  12389. return FALSE;
  12390. }
  12391. } else {
  12392. if ($part !== $this->scheme . '://' . $this->getAuthority() . $this->path) {
  12393. return FALSE;
  12394. }
  12395. }
  12396. $part = preg_split('#[&;]#', self::unescape(strtr((string) strtok('?#'), '+', ' '), '%&;=+'));
  12397. sort($part);
  12398. $query = preg_split('#[&;]#', $this->query);
  12399. sort($query);
  12400. return $part === $query;
  12401. }
  12402. function canonicalize()
  12403. {
  12404. $this->updating();
  12405. $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/');
  12406. $this->host = strtolower(rawurldecode($this->host));
  12407. $this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+');
  12408. }
  12409. function __toString()
  12410. {
  12411. return $this->getAbsoluteUrl();
  12412. }
  12413. static function unescape($s, $reserved = '%;/?:@&=+$,')
  12414. {
  12415. preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
  12416. foreach (array_reverse($matches) as $match) {
  12417. $ch = chr(hexdec($match[0][0]));
  12418. if (strpos($reserved, $ch) === FALSE) {
  12419. $s = substr_replace($s, $ch, $match[0][1] - 1, 3);
  12420. }
  12421. }
  12422. return $s;
  12423. }
  12424. function getRelativeUri()
  12425. {
  12426. trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getRelativeUrl() instead.', E_USER_WARNING);
  12427. return $this->getRelativeUrl();
  12428. }
  12429. function getAbsoluteUri()
  12430. {
  12431. trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getAbsoluteUrl() instead.', E_USER_WARNING);
  12432. return $this->getAbsoluteUrl();
  12433. }
  12434. function getHostUri()
  12435. {
  12436. trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getHostUrl() instead.', E_USER_WARNING);
  12437. return $this->getHostUrl();
  12438. }
  12439. function getBaseUri()
  12440. {
  12441. trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getBaseUrl() instead.', E_USER_WARNING);
  12442. return $this->getBaseUrl();
  12443. }
  12444. }
  12445. class UrlScript extends Url
  12446. {
  12447. private $scriptPath = '/';
  12448. function setScriptPath($value)
  12449. {
  12450. $this->updating();
  12451. $this->scriptPath = (string) $value;
  12452. return $this;
  12453. }
  12454. function getScriptPath()
  12455. {
  12456. return $this->scriptPath;
  12457. }
  12458. function getBasePath()
  12459. {
  12460. $pos = strrpos($this->scriptPath, '/');
  12461. return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1);
  12462. }
  12463. function getPathInfo()
  12464. {
  12465. return (string) substr($this->path, strlen($this->scriptPath));
  12466. }
  12467. }
  12468. use Nette\Security\IAuthenticator;use Nette\Security\IAuthorizator;use Nette\Security\IIdentity;
  12469. class User extends Nette\Object implements IUser
  12470. {
  12471. const MANUAL = 1,
  12472. INACTIVITY = 2,
  12473. BROWSER_CLOSED = 3;
  12474. public $guestRole = 'guest';
  12475. public $authenticatedRole = 'authenticated';
  12476. public $onLoggedIn;
  12477. public $onLoggedOut;
  12478. private $namespace = '';
  12479. private $session;
  12480. private $context;
  12481. function __construct(Nette\DI\IContainer $context)
  12482. {
  12483. $this->context = $context;
  12484. }
  12485. function login($username = NULL, $password = NULL)
  12486. {
  12487. $this->logout(TRUE);
  12488. $credentials = func_get_args();
  12489. $this->setIdentity($this->context->authenticator->authenticate($credentials));
  12490. $this->setAuthenticated(TRUE);
  12491. $this->onLoggedIn($this);
  12492. }
  12493. final function logout($clearIdentity = FALSE)
  12494. {
  12495. if ($this->isLoggedIn()) {
  12496. $this->setAuthenticated(FALSE);
  12497. $this->onLoggedOut($this);
  12498. }
  12499. if ($clearIdentity) {
  12500. $this->setIdentity(NULL);
  12501. }
  12502. }
  12503. final function isLoggedIn()
  12504. {
  12505. $session = $this->getSessionSection(FALSE);
  12506. return $session && $session->authenticated;
  12507. }
  12508. final function getIdentity()
  12509. {
  12510. $session = $this->getSessionSection(FALSE);
  12511. return $session ? $session->identity : NULL;
  12512. }
  12513. function getId()
  12514. {
  12515. $identity = $this->getIdentity();
  12516. return $identity ? $identity->getId() : NULL;
  12517. }
  12518. function setAuthenticator(IAuthenticator $handler)
  12519. {
  12520. $this->context->removeService('authenticator');
  12521. $this->context->authenticator = $handler;
  12522. return $this;
  12523. }
  12524. final function getAuthenticator()
  12525. {
  12526. return $this->context->authenticator;
  12527. }
  12528. function setNamespace($namespace)
  12529. {
  12530. if ($this->namespace !== $namespace) {
  12531. $this->namespace = (string) $namespace;
  12532. $this->session = NULL;
  12533. }
  12534. return $this;
  12535. }
  12536. final function getNamespace()
  12537. {
  12538. return $this->namespace;
  12539. }
  12540. function setExpiration($time, $whenBrowserIsClosed = TRUE, $clearIdentity = FALSE)
  12541. {
  12542. $session = $this->getSessionSection(TRUE);
  12543. if ($time) {
  12544. $time = Nette\DateTime::from($time)->format('U');
  12545. $session->expireTime = $time;
  12546. $session->expireDelta = $time - time();
  12547. } else {
  12548. unset($session->expireTime, $session->expireDelta);
  12549. }
  12550. $session->expireIdentity = (bool) $clearIdentity;
  12551. $session->expireBrowser = (bool) $whenBrowserIsClosed;
  12552. $session->browserCheck = TRUE;
  12553. $session->setExpiration(0, 'browserCheck');
  12554. return $this;
  12555. }
  12556. final function getLogoutReason()
  12557. {
  12558. $session = $this->getSessionSection(FALSE);
  12559. return $session ? $session->reason : NULL;
  12560. }
  12561. protected function getSessionSection($need)
  12562. {
  12563. if ($this->session !== NULL) {
  12564. return $this->session;
  12565. }
  12566. if (!$need && !$this->context->session->exists()) {
  12567. return NULL;
  12568. }
  12569. $this->session = $session = $this->context->session->getSection('Nette.Web.User/' . $this->namespace);
  12570. if (!$session->identity instanceof IIdentity || !is_bool($session->authenticated)) {
  12571. $session->remove();
  12572. }
  12573. if ($session->authenticated && $session->expireBrowser && !$session->browserCheck) {
  12574. $session->reason = self::BROWSER_CLOSED;
  12575. $session->authenticated = FALSE;
  12576. $this->onLoggedOut($this);
  12577. if ($session->expireIdentity) {
  12578. unset($session->identity);
  12579. }
  12580. }
  12581. if ($session->authenticated && $session->expireDelta > 0) {
  12582. if ($session->expireTime < time()) {
  12583. $session->reason = self::INACTIVITY;
  12584. $session->authenticated = FALSE;
  12585. $this->onLoggedOut($this);
  12586. if ($session->expireIdentity) {
  12587. unset($session->identity);
  12588. }
  12589. }
  12590. $session->expireTime = time() + $session->expireDelta;
  12591. }
  12592. if (!$session->authenticated) {
  12593. unset($session->expireTime, $session->expireDelta, $session->expireIdentity,
  12594. $session->expireBrowser, $session->browserCheck, $session->authTime);
  12595. }
  12596. return $this->session;
  12597. }
  12598. protected function setAuthenticated($state)
  12599. {
  12600. $session = $this->getSessionSection(TRUE);
  12601. $session->authenticated = (bool) $state;
  12602. $this->context->session->regenerateId();
  12603. if ($state) {
  12604. $session->reason = NULL;
  12605. $session->authTime = time();
  12606. } else {
  12607. $session->reason = self::MANUAL;
  12608. $session->authTime = NULL;
  12609. }
  12610. return $this;
  12611. }
  12612. protected function setIdentity(IIdentity $identity = NULL)
  12613. {
  12614. $this->getSessionSection(TRUE)->identity = $identity;
  12615. return $this;
  12616. }
  12617. function getRoles()
  12618. {
  12619. if (!$this->isLoggedIn()) {
  12620. return array($this->guestRole);
  12621. }
  12622. $identity = $this->getIdentity();
  12623. return $identity ? $identity->getRoles() : array($this->authenticatedRole);
  12624. }
  12625. final function isInRole($role)
  12626. {
  12627. return in_array($role, $this->getRoles(), TRUE);
  12628. }
  12629. function isAllowed($resource = IAuthorizator::ALL, $privilege = IAuthorizator::ALL)
  12630. {
  12631. $authorizator = $this->context->authorizator;
  12632. foreach ($this->getRoles() as $role) {
  12633. if ($authorizator->isAllowed($role, $resource, $privilege)) {
  12634. return TRUE;
  12635. }
  12636. }
  12637. return FALSE;
  12638. }
  12639. function setAuthorizator(IAuthorizator $handler)
  12640. {
  12641. $this->context->removeService('authorizator');
  12642. $this->context->authorizator = $handler;
  12643. return $this;
  12644. }
  12645. final function getAuthorizator()
  12646. {
  12647. return $this->context->authorizator;
  12648. }
  12649. function setAuthenticationHandler($v)
  12650. {
  12651. trigger_error(__METHOD__ . '() is deprecated; use setAuthenticator() instead.', E_USER_WARNING);
  12652. return $this->setAuthenticator($v);
  12653. }
  12654. function setAuthorizationHandler($v)
  12655. {
  12656. trigger_error(__METHOD__ . '() is deprecated; use setAuthorizator() instead.', E_USER_WARNING);
  12657. return $this->setAuthorizator($v);
  12658. }
  12659. }
  12660. }
  12661. namespace Nette\Iterators {
  12662. use Nette;
  12663. class CachingIterator extends \CachingIterator implements \Countable
  12664. {
  12665. private $counter = 0;
  12666. function __construct($iterator)
  12667. {
  12668. if (is_array($iterator) || $iterator instanceof \stdClass) {
  12669. $iterator = new \ArrayIterator($iterator);
  12670. } elseif ($iterator instanceof \Traversable) {
  12671. if ($iterator instanceof \IteratorAggregate) {
  12672. $iterator = $iterator->getIterator();
  12673. } elseif (!$iterator instanceof \Iterator) {
  12674. $iterator = new \IteratorIterator($iterator);
  12675. }
  12676. } else {
  12677. throw new Nette\InvalidArgumentException("Invalid argument passed to foreach resp. " . __CLASS__ . "; array or Traversable expected, " . (is_object($iterator) ? get_class($iterator) : gettype($iterator)) ." given.");
  12678. }
  12679. parent::__construct($iterator, 0);
  12680. }
  12681. function isFirst($width = NULL)
  12682. {
  12683. return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0);
  12684. }
  12685. function isLast($width = NULL)
  12686. {
  12687. return !$this->hasNext() || ($width && ($this->counter % $width) === 0);
  12688. }
  12689. function isEmpty()
  12690. {
  12691. return $this->counter === 0;
  12692. }
  12693. function isOdd()
  12694. {
  12695. return $this->counter % 2 === 1;
  12696. }
  12697. function isEven()
  12698. {
  12699. return $this->counter % 2 === 0;
  12700. }
  12701. function getCounter()
  12702. {
  12703. return $this->counter;
  12704. }
  12705. function count()
  12706. {
  12707. $inner = $this->getInnerIterator();
  12708. if ($inner instanceof \Countable) {
  12709. return $inner->count();
  12710. } else {
  12711. throw new Nette\NotSupportedException('Iterator is not countable.');
  12712. }
  12713. }
  12714. function next()
  12715. {
  12716. parent::next();
  12717. if (parent::valid()) {
  12718. $this->counter++;
  12719. }
  12720. }
  12721. function rewind()
  12722. {
  12723. parent::rewind();
  12724. $this->counter = parent::valid() ? 1 : 0;
  12725. }
  12726. function getNextKey()
  12727. {
  12728. return $this->getInnerIterator()->key();
  12729. }
  12730. function getNextValue()
  12731. {
  12732. return $this->getInnerIterator()->current();
  12733. }
  12734. function __call($name, $args)
  12735. {
  12736. return Nette\ObjectMixin::call($this, $name, $args);
  12737. }
  12738. function &__get($name)
  12739. {
  12740. return Nette\ObjectMixin::get($this, $name);
  12741. }
  12742. function __set($name, $value)
  12743. {
  12744. return Nette\ObjectMixin::set($this, $name, $value);
  12745. }
  12746. function __isset($name)
  12747. {
  12748. return Nette\ObjectMixin::has($this, $name);
  12749. }
  12750. function __unset($name)
  12751. {
  12752. Nette\ObjectMixin::remove($this, $name);
  12753. }
  12754. }
  12755. class Filter extends \FilterIterator
  12756. {
  12757. private $callback;
  12758. function __construct(\Iterator $iterator, $callback)
  12759. {
  12760. parent::__construct($iterator);
  12761. $this->callback = $callback;
  12762. }
  12763. function accept()
  12764. {
  12765. return call_user_func($this->callback, $this);
  12766. }
  12767. }
  12768. class InstanceFilter extends \FilterIterator implements \Countable
  12769. {
  12770. private $type;
  12771. function __construct(\Iterator $iterator, $type)
  12772. {
  12773. $this->type = $type;
  12774. parent::__construct($iterator);
  12775. }
  12776. function accept()
  12777. {
  12778. return $this->current() instanceof $this->type;
  12779. }
  12780. function count()
  12781. {
  12782. return iterator_count($this);
  12783. }
  12784. }
  12785. class Mapper extends \IteratorIterator
  12786. {
  12787. private $callback;
  12788. function __construct(\Traversable $iterator, $callback)
  12789. {
  12790. parent::__construct($iterator);
  12791. $this->callback = $callback;
  12792. }
  12793. function current()
  12794. {
  12795. return call_user_func($this->callback, parent::current(), parent::key());
  12796. }
  12797. }
  12798. class RecursiveFilter extends \FilterIterator implements \RecursiveIterator
  12799. {
  12800. private $callback;
  12801. private $childrenCallback;
  12802. function __construct(\RecursiveIterator $iterator, $callback, $childrenCallback = NULL)
  12803. {
  12804. parent::__construct($iterator);
  12805. $this->callback = $callback;
  12806. $this->childrenCallback = $childrenCallback;
  12807. }
  12808. function accept()
  12809. {
  12810. return $this->callback === NULL || call_user_func($this->callback, $this);
  12811. }
  12812. function hasChildren()
  12813. {
  12814. return $this->getInnerIterator()->hasChildren()
  12815. && ($this->childrenCallback === NULL || call_user_func($this->childrenCallback, $this));
  12816. }
  12817. function getChildren()
  12818. {
  12819. return new static($this->getInnerIterator()->getChildren(), $this->callback, $this->childrenCallback);
  12820. }
  12821. }
  12822. class Recursor extends \IteratorIterator implements \RecursiveIterator, \Countable
  12823. {
  12824. function hasChildren()
  12825. {
  12826. $obj = $this->current();
  12827. return ($obj instanceof \IteratorAggregate && $obj->getIterator() instanceof \RecursiveIterator)
  12828. || $obj instanceof \RecursiveIterator;
  12829. }
  12830. function getChildren()
  12831. {
  12832. $obj = $this->current();
  12833. return $obj instanceof \IteratorAggregate ? $obj->getIterator() : $obj;
  12834. }
  12835. function count()
  12836. {
  12837. return iterator_count($this);
  12838. }
  12839. }
  12840. }
  12841. namespace Nette\Latte {
  12842. use Nette;
  12843. class Engine extends Nette\Object
  12844. {
  12845. public $parser;
  12846. function __construct()
  12847. {
  12848. $this->parser = new Parser;
  12849. Macros\CoreMacros::install($this->parser);
  12850. $this->parser->addMacro('cache', new Macros\CacheMacro($this->parser));
  12851. Macros\UIMacros::install($this->parser);
  12852. Macros\FormMacros::install($this->parser);
  12853. }
  12854. function __invoke($s)
  12855. {
  12856. $this->parser->context = array(Parser::CONTEXT_TEXT);
  12857. $this->parser->setDelimiters('\\{(?![\\s\'"{}])', '\\}');
  12858. return $this->parser->parse($s);
  12859. }
  12860. }
  12861. class HtmlNode extends Nette\Object
  12862. {
  12863. public $name;
  12864. public $isEmpty = FALSE;
  12865. public $attrs = array();
  12866. public $closing = FALSE;
  12867. public $offset;
  12868. function __construct($name)
  12869. {
  12870. $this->name = $name;
  12871. $this->isEmpty = isset(Nette\Utils\Html::$emptyElements[strtolower($this->name)]);
  12872. }
  12873. }
  12874. class MacroNode extends Nette\Object
  12875. {
  12876. public $macro;
  12877. public $name;
  12878. public $isEmpty = FALSE;
  12879. public $args;
  12880. public $modifiers;
  12881. public $closing = FALSE;
  12882. public $tokenizer;
  12883. public $offset;
  12884. public $parentNode;
  12885. public $content;
  12886. public $data;
  12887. function __construct(IMacro $macro, $name, $args = NULL, $modifiers = NULL, MacroNode $parentNode = NULL)
  12888. {
  12889. $this->macro = $macro;
  12890. $this->name = (string) $name;
  12891. $this->modifiers = (string) $modifiers;
  12892. $this->parentNode = $parentNode;
  12893. $this->tokenizer = new MacroTokenizer($this->args);
  12894. $this->data = new \stdClass;
  12895. $this->setArgs($args);
  12896. }
  12897. function setArgs($args)
  12898. {
  12899. $this->args = (string) $args;
  12900. $this->tokenizer->tokenize($this->args);
  12901. }
  12902. function close($content)
  12903. {
  12904. $this->closing = TRUE;
  12905. $this->content = $content;
  12906. return $this->macro->nodeClosed($this);
  12907. }
  12908. }
  12909. }
  12910. namespace Nette\Latte\Macros {
  12911. use Nette;use Nette\Latte;
  12912. class CacheMacro extends Nette\Object implements Latte\IMacro
  12913. {
  12914. private $used;
  12915. function initialize()
  12916. {
  12917. $this->used = FALSE;
  12918. }
  12919. function finalize()
  12920. {
  12921. if ($this->used) {
  12922. return array('Nette\Latte\Macros\CacheMacro::initRuntime($template, $_g);');
  12923. }
  12924. }
  12925. function nodeOpened(Latte\MacroNode $node)
  12926. {
  12927. $this->used = TRUE;
  12928. $node->isEmpty = FALSE;
  12929. return Latte\PhpWriter::using($node)
  12930. ->write('<?php if (Nette\Latte\Macros\CacheMacro::createCache($netteCacheStorage, %var, $_g->caches, %node.array?)) { ?>',
  12931. Nette\Utils\Strings::random()
  12932. );
  12933. }
  12934. function nodeClosed(Latte\MacroNode $node)
  12935. {
  12936. return '<?php $_l->tmp = array_pop($_g->caches); if (!$_l->tmp instanceof stdClass) $_l->tmp->end(); } ?>';
  12937. }
  12938. static function initRuntime($template, $global)
  12939. {
  12940. if (!empty($global->caches)) {
  12941. end($global->caches)->dependencies[Nette\Caching\Cache::FILES][] = $template->getFile();
  12942. }
  12943. }
  12944. static function createCache(Nette\Caching\IStorage $cacheStorage, $key, & $parents, $args = NULL)
  12945. {
  12946. if ($args) {
  12947. if (array_key_exists('if', $args) && !$args['if']) {
  12948. return $parents[] = (object) NULL;
  12949. }
  12950. $key = array_merge(array($key), array_intersect_key($args, range(0, count($args))));
  12951. }
  12952. if ($parents) {
  12953. end($parents)->dependencies[Nette\Caching\Cache::ITEMS][] = $key;
  12954. }
  12955. $cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Templating.Cache');
  12956. if ($helper = $cache->start($key)) {
  12957. $helper->dependencies = array(
  12958. Nette\Caching\Cache::TAGS => isset($args['tags']) ? $args['tags'] : NULL,
  12959. Nette\Caching\Cache::EXPIRATION => isset($args['expire']) ? $args['expire'] : '+ 7 days',
  12960. );
  12961. $parents[] = $helper;
  12962. }
  12963. return $helper;
  12964. }
  12965. }
  12966. use Nette\Latte\MacroNode;
  12967. class MacroSet extends Nette\Object implements Latte\IMacro
  12968. {
  12969. public $parser;
  12970. private $macros;
  12971. function __construct(Latte\Parser $parser)
  12972. {
  12973. $this->parser = $parser;
  12974. }
  12975. function addMacro($name, $begin, $end = NULL)
  12976. {
  12977. $this->macros[$name] = array($begin, $end);
  12978. $this->parser->addMacro($name, $this);
  12979. }
  12980. static function install(Latte\Parser $parser)
  12981. {
  12982. return new static($parser);
  12983. }
  12984. function initialize()
  12985. {
  12986. }
  12987. function finalize()
  12988. {
  12989. }
  12990. function nodeOpened(MacroNode $node)
  12991. {
  12992. $node->isEmpty = !isset($this->macros[$node->name][1]);
  12993. return $this->compile($node, $this->macros[$node->name][0]);
  12994. }
  12995. function nodeClosed(MacroNode $node)
  12996. {
  12997. return $this->compile($node, $this->macros[$node->name][1]);
  12998. }
  12999. private function compile(MacroNode $node, $def)
  13000. {
  13001. $writer = Latte\PhpWriter::using($node, $this->parser->context);
  13002. if (is_string($def)) {
  13003. $code = $writer->write($def);
  13004. } else {
  13005. $code = callback($def)->invoke($node, $writer);
  13006. if ($code === FALSE) {
  13007. return FALSE;
  13008. }
  13009. }
  13010. return "<?php $code ?>";
  13011. }
  13012. }
  13013. use Nette\Latte\ParseException;
  13014. class CoreMacros extends MacroSet
  13015. {
  13016. static function install(Latte\Parser $parser)
  13017. {
  13018. $me = new static($parser);
  13019. $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf'));
  13020. $me->addMacro('elseif', 'elseif (%node.args):');
  13021. $me->addMacro('else', 'else:');
  13022. $me->addMacro('ifset', 'if (isset(%node.args)):', 'endif');
  13023. $me->addMacro('elseifset', 'elseif (isset(%node.args)):');
  13024. $me->addMacro('foreach', array($me, 'macroForeach'), '$iterations++; endforeach; array_pop($_l->its); $iterator = end($_l->its)');
  13025. $me->addMacro('for', 'for (%node.args):', 'endfor');
  13026. $me->addMacro('while', 'while (%node.args):', 'endwhile');
  13027. $me->addMacro('continueIf', 'if (%node.args) continue');
  13028. $me->addMacro('breakIf', 'if (%node.args) break');
  13029. $me->addMacro('first', 'if ($iterator->isFirst(%node.args)):', 'endif');
  13030. $me->addMacro('last', 'if ($iterator->isLast(%node.args)):', 'endif');
  13031. $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)):', 'endif');
  13032. $me->addMacro('var', array($me, 'macroVar'));
  13033. $me->addMacro('assign', array($me, 'macroVar'));
  13034. $me->addMacro('default', array($me, 'macroVar'));
  13035. $me->addMacro('dump', array($me, 'macroDump'));
  13036. $me->addMacro('debugbreak', array($me, 'macroDebugbreak'));
  13037. $me->addMacro('l', '?>{<?php');
  13038. $me->addMacro('r', '?>}<?php');
  13039. $me->addMacro('_', array($me, 'macroTranslate'));
  13040. $me->addMacro('=', array($me, 'macroExpr'));
  13041. $me->addMacro('?', array($me, 'macroExpr'));
  13042. $me->addMacro('syntax', array($me, 'macroSyntax'), array($me, 'macroSyntax'));
  13043. $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd'));
  13044. $me->addMacro('include', array($me, 'macroInclude'));
  13045. $me->addMacro('use', array($me, 'macroUse'));
  13046. $me->addMacro('@href', NULL, NULL);
  13047. $me->addMacro('@class', array($me, 'macroClass'));
  13048. $me->addMacro('@attr', array($me, 'macroAttr'));
  13049. $me->addMacro('attr', array($me, 'macroOldAttr'));
  13050. }
  13051. function finalize()
  13052. {
  13053. return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, '
  13054. . var_export($this->parser->templateId, TRUE) . ')');
  13055. }
  13056. function macroIf(MacroNode $node, $writer)
  13057. {
  13058. if ($node->data->capture = ($node->args === '')) {
  13059. return 'ob_start()';
  13060. }
  13061. return $writer->write('if (%node.args):');
  13062. }
  13063. function macroEndIf(MacroNode $node, $writer)
  13064. {
  13065. if ($node->data->capture) {
  13066. if ($node->args === '') {
  13067. throw new ParseException('Missing condition in {if} macro.');
  13068. }
  13069. return $writer->write('if (%node.args) ob_end_flush(); else ob_end_clean()');
  13070. }
  13071. return 'endif';
  13072. }
  13073. function macroTranslate(MacroNode $node, $writer)
  13074. {
  13075. return $writer->write('echo %modify', '$template->translate(' . $writer->formatArgs() . ')');
  13076. }
  13077. function macroSyntax(MacroNode $node)
  13078. {
  13079. if ($node->closing) {
  13080. $node->args = 'latte';
  13081. }
  13082. switch ($node->args) {
  13083. case '':
  13084. case 'latte':
  13085. $this->parser->setDelimiters('\\{(?![\\s\'"{}])', '\\}');
  13086. break;
  13087. case 'double':
  13088. $this->parser->setDelimiters('\\{\\{(?![\\s\'"{}])', '\\}\\}');
  13089. break;
  13090. case 'asp':
  13091. $this->parser->setDelimiters('<%\s*', '\s*%>');
  13092. break;
  13093. case 'python':
  13094. $this->parser->setDelimiters('\\{[{%]\s*', '\s*[%}]\\}');
  13095. break;
  13096. case 'off':
  13097. $this->parser->setDelimiters('[^\x00-\xFF]', '');
  13098. break;
  13099. default:
  13100. throw new ParseException("Unknown syntax '$node->args'");
  13101. }
  13102. }
  13103. function macroInclude(MacroNode $node, $writer)
  13104. {
  13105. $code = $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParams(), $_l->templates[%var])',
  13106. $this->parser->templateId);
  13107. if ($node->modifiers) {
  13108. return $writer->write('echo %modify', $code . '->__toString(TRUE)');
  13109. } else {
  13110. return $code . '->render()';
  13111. }
  13112. }
  13113. function macroUse(MacroNode $node, $writer)
  13114. {
  13115. call_user_func(array($node->tokenizer->fetchWord(), 'install'), $this->parser)
  13116. ->initialize();
  13117. }
  13118. function macroCapture(MacroNode $node, $writer)
  13119. {
  13120. $variable = $node->tokenizer->fetchWord();
  13121. if (substr($variable, 0, 1) !== '$') {
  13122. throw new ParseException("Invalid capture block variable '$variable'");
  13123. }
  13124. $node->data->variable = $variable;
  13125. return 'ob_start()';
  13126. }
  13127. function macroCaptureEnd(MacroNode $node, $writer)
  13128. {
  13129. return $writer->write("{$node->data->variable} = %modify", 'ob_get_clean()');
  13130. }
  13131. function macroForeach(MacroNode $node, $writer)
  13132. {
  13133. return '$iterations = 0; foreach ($iterator = $_l->its[] = new Nette\Iterators\CachingIterator('
  13134. . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . '):';
  13135. }
  13136. function macroClass(MacroNode $node, $writer)
  13137. {
  13138. return $writer->write('if ($_l->tmp = trim(implode(" ", array_unique(%node.array)))) echo \' class="\' . %escape($_l->tmp) . \'"\'');
  13139. }
  13140. function macroAttr(MacroNode $node, $writer)
  13141. {
  13142. return $writer->write('echo Nette\Utils\Html::el(NULL, %node.array)->attributes()');
  13143. }
  13144. function macroOldAttr(MacroNode $node)
  13145. {
  13146. return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->');
  13147. }
  13148. function macroDump(MacroNode $node, $writer)
  13149. {
  13150. $args = $writer->formatArgs();
  13151. return $writer->write('Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(%var => $args)" : 'get_defined_vars()')
  13152. . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))', $args);
  13153. }
  13154. function macroDebugbreak()
  13155. {
  13156. return 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()';
  13157. }
  13158. function macroVar(MacroNode $node, $writer)
  13159. {
  13160. $out = '';
  13161. $var = TRUE;
  13162. $tokenizer = $writer->preprocess();
  13163. while ($token = $tokenizer->fetchToken()) {
  13164. if ($var && ($token['type'] === Latte\MacroTokenizer::T_SYMBOL || $token['type'] === Latte\MacroTokenizer::T_VARIABLE)) {
  13165. if ($node->name === 'default') {
  13166. $out .= "'" . ltrim($token['value'], "$") . "'";
  13167. } else {
  13168. $out .= '$' . ltrim($token['value'], "$");
  13169. }
  13170. $var = NULL;
  13171. } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) {
  13172. $out .= $node->name === 'default' ? '=>' : '=';
  13173. $var = FALSE;
  13174. } elseif ($token['value'] === ',' && $token['depth'] === 0) {
  13175. $out .= $node->name === 'default' ? ',' : ';';
  13176. $var = TRUE;
  13177. } elseif ($var === NULL && $node->name === 'default' && $token['type'] !== Latte\MacroTokenizer::T_WHITESPACE) {
  13178. throw new ParseException("Unexpected '$token[value]' in {default $node->args}");
  13179. } else {
  13180. $out .= $writer->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
  13181. }
  13182. }
  13183. return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out;
  13184. }
  13185. function macroExpr(MacroNode $node, $writer)
  13186. {
  13187. return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify', $writer->formatArgs());
  13188. }
  13189. static function includeTemplate($destination, $params, $template)
  13190. {
  13191. if ($destination instanceof Nette\Templating\ITemplate) {
  13192. $tpl = $destination;
  13193. } elseif ($destination == NULL) {
  13194. throw new Nette\InvalidArgumentException("Template file name was not specified.");
  13195. } else {
  13196. $tpl = clone $template;
  13197. if ($template instanceof Nette\Templating\IFileTemplate) {
  13198. if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') {
  13199. $destination = dirname($template->getFile()) . '/' . $destination;
  13200. }
  13201. $tpl->setFile($destination);
  13202. }
  13203. }
  13204. $tpl->setParams($params);
  13205. return $tpl;
  13206. }
  13207. static function initRuntime($template, $templateId)
  13208. {
  13209. if (isset($template->_l)) {
  13210. $local = $template->_l;
  13211. unset($template->_l);
  13212. } else {
  13213. $local = (object) NULL;
  13214. }
  13215. $local->templates[$templateId] = $template;
  13216. if (!isset($template->_g)) {
  13217. $template->_g = (object) NULL;
  13218. }
  13219. return array($local, $template->_g);
  13220. }
  13221. }
  13222. use Nette\Utils\Strings;
  13223. class FormMacros extends MacroSet
  13224. {
  13225. static function install(Latte\Parser $parser)
  13226. {
  13227. $me = new static($parser);
  13228. $me->addMacro('form', '$form = $control[%node.word]; echo $form->getElementPrototype()->addAttributes(%node.array)->startTag()',
  13229. 'echo $form->getElementPrototype()->endTag()');
  13230. $me->addMacro('label', array($me, 'macroLabel'), '?></label><?php');
  13231. $me->addMacro('input', 'echo $form[%node.word]->getControl()->addAttributes(%node.array)');
  13232. }
  13233. function macroLabel(MacroNode $node, $writer)
  13234. {
  13235. $cmd = 'if ($_label = $form[%node.word]->getLabel()) echo $_label->addAttributes(%node.array)';
  13236. if ($node->isEmpty = (substr($node->args, -1) === '/')) {
  13237. $node->setArgs(substr($node->args, 0, -1));
  13238. return $writer->write($cmd);
  13239. } else {
  13240. return $writer->write($cmd . '->startTag()');
  13241. }
  13242. }
  13243. }
  13244. class UIMacros extends MacroSet
  13245. {
  13246. const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF]*';
  13247. private $namedBlocks = array();
  13248. private $extends;
  13249. static function install(Latte\Parser $parser)
  13250. {
  13251. $me = new static($parser);
  13252. $me->addMacro('include', array($me, 'macroInclude'));
  13253. $me->addMacro('includeblock', array($me, 'macroIncludeBlock'));
  13254. $me->addMacro('extends', array($me, 'macroExtends'));
  13255. $me->addMacro('layout', array($me, 'macroExtends'));
  13256. $me->addMacro('block', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
  13257. $me->addMacro('define', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
  13258. $me->addMacro('snippet', array($me, 'macroBlock'), array($me, 'macroBlockEnd'));
  13259. $me->addMacro('ifset', array($me, 'macroIfset'), 'endif');
  13260. $me->addMacro('widget', array($me, 'macroControl'));
  13261. $me->addMacro('control', array($me, 'macroControl'));
  13262. $me->addMacro('@href', function(MacroNode $node, $writer) use($me) {
  13263. return ' ?> href="<?php ' . $me->macroLink($node, $writer) . ' ?>"<?php ';
  13264. });
  13265. $me->addMacro('plink', array($me, 'macroLink'));
  13266. $me->addMacro('link', array($me, 'macroLink'));
  13267. $me->addMacro('ifCurrent', array($me, 'macroIfCurrent'), 'endif');
  13268. $me->addMacro('contentType', array($me, 'macroContentType'));
  13269. $me->addMacro('status', '$netteHttpResponse->setCode(%node.args)');
  13270. }
  13271. function initialize()
  13272. {
  13273. $this->namedBlocks = array();
  13274. $this->extends = NULL;
  13275. }
  13276. function finalize()
  13277. {
  13278. try {
  13279. $this->parser->writeMacro('/block');
  13280. } catch (ParseException $e) {
  13281. }
  13282. $epilog = $prolog = array();
  13283. if ($this->namedBlocks) {
  13284. foreach ($this->namedBlocks as $name => $code) {
  13285. $func = '_lb' . substr(md5($this->parser->templateId . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
  13286. $snippet = $name[0] === '_';
  13287. $prolog[] = "//\n// block $name\n//\n"
  13288. . "if (!function_exists(\$_l->blocks[" . var_export($name, TRUE) . "][] = '$func')) { "
  13289. . "function $func(\$_l, \$_args) { "
  13290. . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v')
  13291. . ($snippet ? '; $control->validateControl(' . var_export(substr($name, 1), TRUE) . ')' : '')
  13292. . "\n?>$code<?php\n}}";
  13293. }
  13294. $prolog[] = "//\n// end of blocks\n//";
  13295. }
  13296. if ($this->namedBlocks || $this->extends) {
  13297. $prolog[] = "// template extending and snippets support";
  13298. if (is_bool($this->extends)) {
  13299. $prolog[] = '$_l->extends = ' . var_export($this->extends, TRUE) . '; unset($_extends, $template->_extends);';
  13300. } else {
  13301. $prolog[] = '$_l->extends = empty($template->_extends) ? FALSE : $template->_extends; unset($_extends, $template->_extends);';
  13302. }
  13303. $prolog[] = '
  13304. if ($_l->extends) {
  13305. ob_start();
  13306. } elseif (!empty($control->snippetMode)) {
  13307. return Nette\Latte\Macros\UIMacros::renderSnippets($control, $_l, get_defined_vars());
  13308. }';
  13309. $epilog[] = '
  13310. // template extending support
  13311. if ($_l->extends) {
  13312. ob_end_clean();
  13313. Nette\Latte\Macros\CoreMacros::includeTemplate($_l->extends, get_defined_vars(), $template)->render();
  13314. }';
  13315. } else {
  13316. $prolog[] = '
  13317. // snippets support
  13318. if (!empty($control->snippetMode)) {
  13319. return Nette\Latte\Macros\UIMacros::renderSnippets($control, $_l, get_defined_vars());
  13320. }';
  13321. }
  13322. return array(implode("\n\n", $prolog), implode("\n", $epilog));
  13323. }
  13324. function macroInclude(MacroNode $node, $writer)
  13325. {
  13326. $destination = $node->tokenizer->fetchWord();
  13327. if (substr($destination, 0, 1) !== '#') {
  13328. return FALSE;
  13329. }
  13330. $destination = ltrim($destination, '#');
  13331. if (!Strings::match($destination, '#^\$?' . self::RE_IDENTIFIER . '$#')) {
  13332. throw new ParseException("Included block name must be alphanumeric string, '$destination' given.");
  13333. }
  13334. $parent = $destination === 'parent';
  13335. if ($destination === 'parent' || $destination === 'this') {
  13336. $item = $node->parentNode;
  13337. while ($item && $item->name !== 'block' && !isset($item->data->name)) $item = $item->parentNode;
  13338. if (!$item) {
  13339. throw new ParseException("Cannot include $destination block outside of any block.");
  13340. }
  13341. $destination = $item->data->name;
  13342. }
  13343. $name = $destination[0] === '$' ? $destination : var_export($destination, TRUE);
  13344. if (isset($this->namedBlocks[$destination]) && !$parent) {
  13345. $cmd = "call_user_func(reset(\$_l->blocks[$name]), \$_l, %node.array? + \$template->getParams())";
  13346. } else {
  13347. $cmd = 'Nette\Latte\Macros\UIMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_l, $name, %node.array? + \$template->getParams())";
  13348. }
  13349. if ($node->modifiers) {
  13350. return $writer->write("ob_start(); $cmd; echo %modify", 'ob_get_clean()');
  13351. } else {
  13352. return $writer->write($cmd);
  13353. }
  13354. }
  13355. function macroIncludeBlock(MacroNode $node, $writer)
  13356. {
  13357. return $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + get_defined_vars(), $_l->templates[%var])->render()',
  13358. $this->parser->templateId);
  13359. }
  13360. function macroExtends(MacroNode $node, $writer)
  13361. {
  13362. if (!$node->args) {
  13363. throw new ParseException("Missing destination in {extends}");
  13364. }
  13365. if (!empty($node->parentNode)) {
  13366. throw new ParseException("{extends} must be placed outside any macro.");
  13367. }
  13368. if ($this->extends !== NULL) {
  13369. throw new ParseException("Multiple {extends} declarations are not allowed.");
  13370. }
  13371. $this->extends = $node->args !== 'none';
  13372. return $this->extends ? '$_l->extends = ' . ($node->args === 'auto' ? '$layout' : $writer->formatArgs()) : '';
  13373. }
  13374. function macroBlock(MacroNode $node, $writer)
  13375. {
  13376. $name = $node->tokenizer->fetchWord();
  13377. if ($node->name === 'block' && $name === FALSE) {
  13378. return $node->modifiers === '' ? '' : 'ob_start()';
  13379. }
  13380. $node->data->name = $name = ltrim($name, '#');
  13381. $node->data->end = '';
  13382. if ($name == NULL) {
  13383. if ($node->name !== 'snippet') {
  13384. throw new ParseException("Missing block name.");
  13385. }
  13386. } elseif (!Strings::match($name, '#^' . self::RE_IDENTIFIER . '$#')) {
  13387. if ($node->name === 'snippet') {
  13388. $parent = $node->parentNode;
  13389. while ($parent && $parent->name !== 'snippet') $parent = $parent->parentNode;
  13390. if (!$parent) {
  13391. throw new ParseException("Dynamic snippets are allowed only inside static snippet.");
  13392. }
  13393. $parent->data->dynamic = TRUE;
  13394. $tag = trim($node->tokenizer->fetchWord(), '<>');
  13395. $tag = $tag ? $tag : 'div';
  13396. $node->data->leave = TRUE;
  13397. $node->data->end = "\$_dynSnippets[\$_dynSnippetId] = ob_get_flush() ?>\n</$tag><?php";
  13398. return $writer->write("?>\n<$tag id=\"<?php echo \$_dynSnippetId = \$control->getSnippetId({$writer->formatWord($name)}) ?>\"><?php ob_start()");
  13399. } else {
  13400. $node->data->leave = TRUE;
  13401. $fname = $writer->formatWord($name);
  13402. $node->data->end = "}} call_user_func(reset(\$_l->blocks[$fname]), \$_l, get_defined_vars())";
  13403. $func = '_lb' . substr(md5($this->parser->templateId . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name);
  13404. return "//\n// block $name\n//\n"
  13405. . "if (!function_exists(\$_l->blocks[$fname][] = '$func')) { "
  13406. . "function $func(\$_l, \$_args) { "
  13407. . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v');
  13408. }
  13409. }
  13410. if ($node->name === 'snippet') {
  13411. $node->data->name = $name = '_' . $name;
  13412. }
  13413. if (isset($this->namedBlocks[$name])) {
  13414. throw new ParseException("Cannot redeclare static block '$name'");
  13415. }
  13416. $top = empty($node->parentNode);
  13417. $this->namedBlocks[$name] = TRUE;
  13418. $include = 'call_user_func(reset($_l->blocks[%var]), $_l, ' . ($node->name === 'snippet' ? '$template->getParams()' : 'get_defined_vars()') . ')';
  13419. if ($node->modifiers) {
  13420. $include = "ob_start(); $include; echo %modify";
  13421. }
  13422. if ($node->name === 'snippet') {
  13423. $tag = trim($node->tokenizer->fetchWord(), '<>');
  13424. $tag = $tag ? $tag : 'div';
  13425. return $writer->write("?>\n<$tag id=\"<?php echo \$control->getSnippetId(%var) ?>\"><?php $include ?>\n</$tag><?php ",
  13426. (string) substr($name, 1), $name, 'ob_get_clean()'
  13427. );
  13428. } elseif ($node->name === 'define') {
  13429. return '';
  13430. } elseif (!$top) {
  13431. return $writer->write($include, $name, 'ob_get_clean()');
  13432. } elseif ($this->extends) {
  13433. return '';
  13434. } else {
  13435. return $writer->write("if (!\$_l->extends) { $include; }", $name, 'ob_get_clean()');
  13436. }
  13437. }
  13438. function macroBlockEnd(MacroNode $node, $writer)
  13439. {
  13440. if (isset($node->data->name)) {
  13441. if (empty($node->data->leave)) {
  13442. if (!empty($node->data->dynamic)) {
  13443. $node->content .= '<?php if (isset($_dynSnippets)) return $_dynSnippets; ?>';
  13444. }
  13445. $this->namedBlocks[$node->data->name] = $node->content;
  13446. $node->content = '';
  13447. }
  13448. return $node->data->end;
  13449. } elseif ($node->modifiers) {
  13450. return $writer->write('echo %modify', 'ob_get_clean()');
  13451. }
  13452. }
  13453. function macroIfset(MacroNode $node, $writer)
  13454. {
  13455. if (strpos($node->args, '#') === FALSE) {
  13456. return FALSE;
  13457. }
  13458. $list = array();
  13459. while (($name = $node->tokenizer->fetchWord()) !== FALSE) {
  13460. $list[] = $name[0] === '#' ? '$_l->blocks["' . substr($name, 1) . '"]' : $name;
  13461. }
  13462. return 'if (isset(' . implode(', ', $list) . ')):';
  13463. }
  13464. function macroControl(MacroNode $node, $writer)
  13465. {
  13466. $pair = $node->tokenizer->fetchWord();
  13467. if ($pair === FALSE) {
  13468. throw new ParseException("Missing control name in {control}");
  13469. }
  13470. $pair = explode(':', $pair, 2);
  13471. $name = $writer->formatWord($pair[0]);
  13472. $method = isset($pair[1]) ? ucfirst($pair[1]) : '';
  13473. $method = Strings::match($method, '#^(' . self::RE_IDENTIFIER . '|)$#') ? "render$method" : "{\"render$method\"}";
  13474. $param = $writer->formatArray();
  13475. if (strpos($node->args, '=>') === FALSE) {
  13476. $param = substr($param, 6, -1);
  13477. }
  13478. return ($name[0] === '$' ? "if (is_object($name)) \$_ctrl = $name; else " : '')
  13479. . '$_ctrl = $control->getWidget(' . $name . '); '
  13480. . 'if ($_ctrl instanceof Nette\Application\UI\IPartiallyRenderable) $_ctrl->validateControl(); '
  13481. . "\$_ctrl->$method($param)";
  13482. }
  13483. function macroLink(MacroNode $node, $writer)
  13484. {
  13485. return $writer->write('echo %escape(' . ($node->name === 'plink' ? '$presenter' : '$control') . '->link(%node.word, %node.array?))');
  13486. }
  13487. function macroIfCurrent(MacroNode $node, $writer)
  13488. {
  13489. return $writer->write(($node->args ? 'try { $presenter->link(%node.word, %node.array?); } catch (Nette\Application\UI\InvalidLinkException $e) {}' : '')
  13490. . '; if ($presenter->getLastCreatedRequestFlag("current")):');
  13491. }
  13492. function macroContentType(MacroNode $node, $writer)
  13493. {
  13494. if (strpos($node->args, 'html') !== FALSE) {
  13495. $this->parser->context = array(Latte\Parser::CONTEXT_TEXT);
  13496. } elseif (strpos($node->args, 'xml') !== FALSE) {
  13497. $this->parser->context = array(Latte\Parser::CONTEXT_NONE, 'xml');
  13498. } elseif (strpos($node->args, 'javascript') !== FALSE) {
  13499. $this->parser->context = array(Latte\Parser::CONTEXT_NONE, 'js');
  13500. } elseif (strpos($node->args, 'css') !== FALSE) {
  13501. $this->parser->context = array(Latte\Parser::CONTEXT_NONE, 'css');
  13502. } elseif (strpos($node->args, 'plain') !== FALSE) {
  13503. $this->parser->context = array(Latte\Parser::CONTEXT_NONE, 'text');
  13504. } else {
  13505. $this->parser->context = array(Latte\Parser::CONTEXT_NONE);
  13506. }
  13507. if (strpos($node->args, '/')) {
  13508. return $writer->write('$netteHttpResponse->setHeader("Content-Type", %node.word)');
  13509. }
  13510. }
  13511. static function callBlock($context, $name, $params)
  13512. {
  13513. if (empty($context->blocks[$name])) {
  13514. throw new Nette\InvalidStateException("Cannot include undefined block '$name'.");
  13515. }
  13516. $block = reset($context->blocks[$name]);
  13517. $block($context, $params);
  13518. }
  13519. static function callBlockParent($context, $name, $params)
  13520. {
  13521. if (empty($context->blocks[$name]) || ($block = next($context->blocks[$name])) === FALSE) {
  13522. throw new Nette\InvalidStateException("Cannot include undefined parent block '$name'.");
  13523. }
  13524. $block($context, $params);
  13525. }
  13526. static function renderSnippets($control, $local, $params)
  13527. {
  13528. $control->snippetMode = FALSE;
  13529. $payload = $control->getPresenter()->getPayload();
  13530. if (isset($local->blocks)) {
  13531. foreach ($local->blocks as $name => $function) {
  13532. if ($name[0] !== '_' || !$control->isControlInvalid(substr($name, 1))) {
  13533. continue;
  13534. }
  13535. ob_start();
  13536. $function = reset($function);
  13537. $snippets = $function($local, $params);
  13538. $payload->snippets[$id = $control->getSnippetId(substr($name, 1))] = ob_get_clean();
  13539. if ($snippets) {
  13540. $payload->snippets += $snippets;
  13541. unset($payload->snippets[$id]);
  13542. }
  13543. }
  13544. }
  13545. if ($control instanceof Nette\Application\UI\Control) {
  13546. foreach ($control->getComponents(FALSE, 'Nette\Application\UI\Control') as $child) {
  13547. if ($child->isControlInvalid()) {
  13548. $child->snippetMode = TRUE;
  13549. $child->render();
  13550. $child->snippetMode = FALSE;
  13551. }
  13552. }
  13553. }
  13554. }
  13555. }
  13556. }
  13557. namespace Nette\Utils {
  13558. use Nette;use Nette\Utils\Strings;
  13559. class Tokenizer extends Nette\Object
  13560. {
  13561. public $tokens;
  13562. public $position = 0;
  13563. public $ignored = array();
  13564. private $input;
  13565. private $re;
  13566. private $types;
  13567. public $current;
  13568. function __construct(array $patterns, $flags = '')
  13569. {
  13570. $this->re = '~(' . implode(')|(', $patterns) . ')~A' . $flags;
  13571. $keys = array_keys($patterns);
  13572. $this->types = $keys === range(0, count($patterns) - 1) ? FALSE : $keys;
  13573. }
  13574. function tokenize($input)
  13575. {
  13576. $this->input = $input;
  13577. if ($this->types) {
  13578. $this->tokens = Strings::matchAll($input, $this->re);
  13579. $len = 0;
  13580. $count = count($this->types);
  13581. $line = 1;
  13582. foreach ($this->tokens as & $match) {
  13583. $type = NULL;
  13584. for ($i = 1; $i <= $count; $i++) {
  13585. if (!isset($match[$i])) {
  13586. break;
  13587. } elseif ($match[$i] != NULL) {
  13588. $type = $this->types[$i - 1]; break;
  13589. }
  13590. }
  13591. $match = self::createToken($match[0], $type, $line);
  13592. $len += strlen($match['value']);
  13593. $line += substr_count($match['value'], "\n");
  13594. }
  13595. if ($len !== strlen($input)) {
  13596. $errorOffset = $len;
  13597. }
  13598. } else {
  13599. $this->tokens = Strings::split($input, $this->re, PREG_SPLIT_NO_EMPTY);
  13600. if ($this->tokens && !Strings::match(end($this->tokens), $this->re)) {
  13601. $tmp = Strings::split($this->input, $this->re, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
  13602. list(, $errorOffset) = end($tmp);
  13603. }
  13604. }
  13605. if (isset($errorOffset)) {
  13606. $line = $errorOffset ? substr_count($this->input, "\n", 0, $errorOffset) + 1 : 1;
  13607. $col = $errorOffset - strrpos(substr($this->input, 0, $errorOffset), "\n") + 1;
  13608. $token = str_replace("\n", '\n', substr($input, $errorOffset, 10));
  13609. throw new TokenizerException("Unexpected '$token' on line $line, column $col.");
  13610. }
  13611. return $this->tokens;
  13612. }
  13613. static function createToken($value, $type = NULL, $line = NULL)
  13614. {
  13615. return array('value' => $value, 'type' => $type, 'line' => $line);
  13616. }
  13617. function getOffset($i)
  13618. {
  13619. $tokens = Strings::split($this->input, $this->re, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
  13620. $offset = isset($tokens[$i]) ? $tokens[$i][1] : strlen($this->input);
  13621. return array(
  13622. $offset,
  13623. ($offset ? substr_count($this->input, "\n", 0, $offset) + 1 : 1),
  13624. $offset - strrpos(substr($this->input, 0, $offset), "\n"),
  13625. );
  13626. }
  13627. function fetch()
  13628. {
  13629. $args = func_get_args();
  13630. return $this->scan($args, TRUE);
  13631. }
  13632. function fetchToken()
  13633. {
  13634. $args = func_get_args();
  13635. return $this->scan($args, TRUE) === FALSE ? FALSE : $this->current;
  13636. }
  13637. function fetchAll()
  13638. {
  13639. $args = func_get_args();
  13640. return $this->scan($args, FALSE);
  13641. }
  13642. function fetchUntil($arg)
  13643. {
  13644. $args = func_get_args();
  13645. return $this->scan($args, FALSE, TRUE, TRUE);
  13646. }
  13647. function isNext($arg)
  13648. {
  13649. $args = func_get_args();
  13650. return (bool) $this->scan($args, TRUE, FALSE);
  13651. }
  13652. function isPrev($arg)
  13653. {
  13654. $args = func_get_args();
  13655. return (bool) $this->scan($args, TRUE, FALSE, FALSE, TRUE);
  13656. }
  13657. function hasNext()
  13658. {
  13659. return isset($this->tokens[$this->position]);
  13660. }
  13661. function hasPrev()
  13662. {
  13663. return $this->position > 1;
  13664. }
  13665. function isCurrent($arg)
  13666. {
  13667. $args = func_get_args();
  13668. if (is_array($this->current)) {
  13669. return in_array($this->current['value'], $args, TRUE)
  13670. || in_array($this->current['type'], $args, TRUE);
  13671. } else {
  13672. return in_array($this->current, $args, TRUE);
  13673. }
  13674. }
  13675. private function scan($wanted, $first, $advance = TRUE, $neg = FALSE, $prev = FALSE)
  13676. {
  13677. $res = FALSE;
  13678. $pos = $this->position + ($prev ? -2 : 0);
  13679. while (isset($this->tokens[$pos])) {
  13680. $token = $this->tokens[$pos];
  13681. $pos += $prev ? -1 : 1;
  13682. $value = is_array($token) ? $token['value'] : $token;
  13683. $type = is_array($token) ? $token['type'] : $token;
  13684. if (!$wanted || (in_array($value, $wanted, TRUE) || in_array($type, $wanted, TRUE)) ^ $neg) {
  13685. if ($advance) {
  13686. $this->position = $pos;
  13687. $this->current = $token;
  13688. }
  13689. $res .= $value;
  13690. if ($first) {
  13691. break;
  13692. }
  13693. } elseif ($neg || !in_array($type, $this->ignored, TRUE)) {
  13694. break;
  13695. }
  13696. }
  13697. return $res;
  13698. }
  13699. }
  13700. class TokenizerException extends \Exception
  13701. {
  13702. }
  13703. }
  13704. namespace Nette\Latte {
  13705. use Nette;
  13706. class MacroTokenizer extends Nette\Utils\Tokenizer
  13707. {
  13708. const T_WHITESPACE = 1,
  13709. T_COMMENT = 2,
  13710. T_SYMBOL = 3,
  13711. T_NUMBER = 4,
  13712. T_VARIABLE = 5,
  13713. T_STRING = 6,
  13714. T_CAST = 7,
  13715. T_KEYWORD = 8,
  13716. T_CHAR = 9;
  13717. function __construct($input)
  13718. {
  13719. parent::__construct(array(
  13720. self::T_WHITESPACE => '\s+',
  13721. self::T_COMMENT => '(?s)/\*.*?\*/',
  13722. self::T_STRING => Parser::RE_STRING,
  13723. self::T_KEYWORD => '(?:true|false|null|and|or|xor|clone|new|instanceof|return|continue|break|[A-Z_][A-Z0-9_]{2,})(?![\w\pL_])',
  13724. self::T_CAST => '\([a-z]+\)',
  13725. self::T_VARIABLE => '\$[\w\pL_]+',
  13726. self::T_NUMBER => '[+-]?[0-9]+(?:\.[0-9]+)?(?:e[0-9]+)?',
  13727. self::T_SYMBOL => '[\w\pL_]+(?:-[\w\pL_]+)*',
  13728. self::T_CHAR => '::|=>|[^"\']',
  13729. ), 'u');
  13730. $this->ignored = array(self::T_COMMENT, self::T_WHITESPACE);
  13731. $this->tokenize($input);
  13732. }
  13733. function fetchWord()
  13734. {
  13735. $word = $this->fetchUntil(self::T_WHITESPACE, ',');
  13736. $this->fetch(',');
  13737. $this->fetchAll(self::T_WHITESPACE, self::T_COMMENT);
  13738. return $word;
  13739. }
  13740. }
  13741. }
  13742. namespace Nette\Templating {
  13743. use Nette;
  13744. class FilterException extends Nette\InvalidStateException
  13745. {
  13746. public $sourceFile;
  13747. public $sourceLine;
  13748. function __construct($message, $code = 0, $sourceLine = 0)
  13749. {
  13750. $this->sourceLine = (int) $sourceLine;
  13751. parent::__construct($message, $code);
  13752. }
  13753. function setSourceFile($file)
  13754. {
  13755. $this->sourceFile = (string) $file;
  13756. $this->message = rtrim($this->message, '.') . " in " . str_replace(dirname(dirname($file)), '...', $file)
  13757. . ($this->sourceLine ? ":$this->sourceLine" : '');
  13758. }
  13759. }
  13760. }
  13761. namespace Nette\Latte {
  13762. use Nette;
  13763. class ParseException extends Nette\Templating\FilterException
  13764. {
  13765. }
  13766. use Nette\Utils\Strings;
  13767. class Parser extends Nette\Object
  13768. {
  13769. const RE_STRING = '\'(?:\\\\.|[^\'\\\\])*\'|"(?:\\\\.|[^"\\\\])*"';
  13770. const N_PREFIX = 'n:';
  13771. private $macroRe;
  13772. private $input;
  13773. private $output;
  13774. private $offset;
  13775. private $macros;
  13776. private $macroHandlers;
  13777. private $htmlNodes = array();
  13778. private $macroNodes = array();
  13779. public $context;
  13780. public $templateId;
  13781. const CONTEXT_TEXT = 'text',
  13782. CONTEXT_CDATA = 'cdata',
  13783. CONTEXT_TAG = 'tag',
  13784. CONTEXT_ATTRIBUTE = 'attribute',
  13785. CONTEXT_NONE = 'none',
  13786. CONTEXT_COMMENT = 'comment';
  13787. function __construct()
  13788. {
  13789. $this->macroHandlers = new \SplObjectStorage;
  13790. $this->setDelimiters('\\{(?![\\s\'"{}])', '\\}');
  13791. $this->context = array(self::CONTEXT_NONE, 'text');
  13792. }
  13793. function addMacro($name, IMacro $macro)
  13794. {
  13795. $this->macros[$name][] = $macro;
  13796. $this->macroHandlers->attach($macro);
  13797. return $this;
  13798. }
  13799. function parse($s)
  13800. {
  13801. if (!Strings::checkEncoding($s)) {
  13802. throw new ParseException('Template is not valid UTF-8 stream.');
  13803. }
  13804. $s = str_replace("\r\n", "\n", $s);
  13805. $this->templateId = Strings::random();
  13806. $this->input = & $s;
  13807. $this->offset = 0;
  13808. $this->output = '';
  13809. $this->htmlNodes = $this->macroNodes = array();
  13810. foreach ($this->macroHandlers as $handler) {
  13811. $handler->initialize($this);
  13812. }
  13813. $len = strlen($s);
  13814. try {
  13815. while ($this->offset < $len) {
  13816. $matches = $this->{"context".$this->context[0]}();
  13817. if (!$matches) {
  13818. break;
  13819. } elseif (!empty($matches['comment'])) {
  13820. } elseif (!empty($matches['macro'])) {
  13821. list($macroName, $macroArgs, $macroModifiers) = $this->parseMacro($matches['macro']);
  13822. $isRightmost = $this->offset >= $len || $this->input[$this->offset] === "\n";
  13823. $this->writeMacro($macroName, $macroArgs, $macroModifiers, $isRightmost);
  13824. } else {
  13825. $this->output .= $matches[0];
  13826. }
  13827. }
  13828. } catch (ParseException $e) {
  13829. if (!$e->sourceLine) {
  13830. $e->sourceLine = $this->getLine();
  13831. }
  13832. throw $e;
  13833. }
  13834. $this->output .= substr($this->input, $this->offset);
  13835. foreach ($this->htmlNodes as $node) {
  13836. if (!empty($node->attrs)) {
  13837. throw new ParseException("Missing end tag </$node->name> for macro-attribute " . self::N_PREFIX
  13838. . implode(' and ' . self::N_PREFIX, array_keys($node->attrs)) . ".", 0, $this->getLine());
  13839. }
  13840. }
  13841. $prologs = $epilogs = '';
  13842. foreach ($this->macroHandlers as $handler) {
  13843. $res = $handler->finalize();
  13844. $prologs .= isset($res[0]) ? "<?php $res[0]\n?>" : '';
  13845. $epilogs .= isset($res[1]) ? "<?php $res[1]\n?>" : '';
  13846. }
  13847. $this->output = ($prologs ? $prologs . "<?php\n//\n// main template\n//\n?>\n" : '') . $this->output . $epilogs;
  13848. if ($this->macroNodes) {
  13849. throw new ParseException("There are unclosed macros.", 0, $this->getLine());
  13850. }
  13851. return $this->output;
  13852. }
  13853. private function contextText()
  13854. {
  13855. $matches = $this->match('~
  13856. (?:(?<=\n|^)[ \t]*)?<(?P<closing>/?)(?P<tag>[a-z0-9:]+)| ## begin of HTML tag <tag </tag - ignores <!DOCTYPE
  13857. <(?P<htmlcomment>!--)| ## begin of HTML comment <!--
  13858. '.$this->macroRe.' ## curly tag
  13859. ~xsi');
  13860. if (!$matches || !empty($matches['macro']) || !empty($matches['comment'])) {
  13861. } elseif (!empty($matches['htmlcomment'])) {
  13862. $this->context = array(self::CONTEXT_COMMENT);
  13863. } elseif (empty($matches['closing'])) {
  13864. $this->htmlNodes[] = $node = new HtmlNode($matches['tag']);
  13865. $node->offset = strlen($this->output);
  13866. $this->context = array(self::CONTEXT_TAG);
  13867. } else {
  13868. do {
  13869. $node = array_pop($this->htmlNodes);
  13870. if (!$node) {
  13871. $node = new HtmlNode($matches['tag']);
  13872. }
  13873. } while (strcasecmp($node->name, $matches['tag']));
  13874. $this->htmlNodes[] = $node;
  13875. $node->closing = TRUE;
  13876. $node->offset = strlen($this->output);
  13877. $this->context = array(self::CONTEXT_TAG);
  13878. }
  13879. return $matches;
  13880. }
  13881. private function contextCData()
  13882. {
  13883. $node = end($this->htmlNodes);
  13884. $matches = $this->match('~
  13885. </'.$node->name.'(?![a-z0-9:])| ## end HTML tag </tag
  13886. '.$this->macroRe.' ## curly tag
  13887. ~xsi');
  13888. if ($matches && empty($matches['macro']) && empty($matches['comment'])) {
  13889. $node->closing = TRUE;
  13890. $node->offset = strlen($this->output);
  13891. $this->context = array(self::CONTEXT_TAG);
  13892. }
  13893. return $matches;
  13894. }
  13895. private function contextTag()
  13896. {
  13897. $matches = $this->match('~
  13898. (?P<end>\ ?/?>)(?P<tagnewline>[ \t]*\n)?| ## end of HTML tag
  13899. '.$this->macroRe.'| ## curly tag
  13900. \s*(?P<attr>[^\s/>={]+)(?:\s*=\s*(?P<value>["\']|[^\s/>{]+))? ## begin of HTML attribute
  13901. ~xsi');
  13902. if (!$matches || !empty($matches['macro']) || !empty($matches['comment'])) {
  13903. } elseif (!empty($matches['end'])) {
  13904. $node = end($this->htmlNodes);
  13905. $isEmpty = !$node->closing && (strpos($matches['end'], '/') !== FALSE || $node->isEmpty);
  13906. if ($isEmpty) {
  13907. $matches[0] = (Nette\Utils\Html::$xhtml ? ' />' : '>')
  13908. . (isset($matches['tagnewline']) ? $matches['tagnewline'] : '');
  13909. }
  13910. if (!empty($node->attrs)) {
  13911. $code = substr($this->output, $node->offset) . $matches[0];
  13912. $this->output = substr($this->output, 0, $node->offset);
  13913. $this->writeAttrsMacro($code, $node->attrs, $node->closing);
  13914. if ($isEmpty) {
  13915. $this->writeAttrsMacro('', $node->attrs, TRUE);
  13916. }
  13917. $matches[0] = '';
  13918. }
  13919. if ($isEmpty) {
  13920. $node->closing = TRUE;
  13921. }
  13922. if (!$node->closing && (strcasecmp($node->name, 'script') === 0 || strcasecmp($node->name, 'style') === 0)) {
  13923. $this->context = array(self::CONTEXT_CDATA, strcasecmp($node->name, 'style') ? 'js' : 'css');
  13924. } else {
  13925. $this->context = array(self::CONTEXT_TEXT);
  13926. if ($node->closing) {
  13927. array_pop($this->htmlNodes);
  13928. }
  13929. }
  13930. } else {
  13931. $name = $matches['attr'];
  13932. $value = isset($matches['value']) ? $matches['value'] : '';
  13933. $node = end($this->htmlNodes);
  13934. if (Strings::startsWith($name, self::N_PREFIX)) {
  13935. $name = substr($name, strlen(self::N_PREFIX));
  13936. if ($value === '"' || $value === "'") {
  13937. if ($matches = $this->match('~(.*?)' . $value . '~xsi')) {
  13938. $value = $matches[1];
  13939. }
  13940. }
  13941. $node->attrs[$name] = $value;
  13942. $matches[0] = '';
  13943. } elseif ($value === '"' || $value === "'") {
  13944. $this->context = array(self::CONTEXT_ATTRIBUTE, $name, $value);
  13945. }
  13946. }
  13947. return $matches;
  13948. }
  13949. private function contextAttribute()
  13950. {
  13951. $matches = $this->match('~
  13952. (' . $this->context[2] . ')| ## 1) end of HTML attribute
  13953. '.$this->macroRe.' ## curly tag
  13954. ~xsi');
  13955. if ($matches && empty($matches['macro']) && empty($matches['comment'])) {
  13956. $this->context = array(self::CONTEXT_TAG);
  13957. }
  13958. return $matches;
  13959. }
  13960. private function contextComment()
  13961. {
  13962. $matches = $this->match('~
  13963. (--\s*>)| ## 1) end of HTML comment
  13964. '.$this->macroRe.' ## curly tag
  13965. ~xsi');
  13966. if ($matches && empty($matches['macro']) && empty($matches['comment'])) {
  13967. $this->context = array(self::CONTEXT_TEXT);
  13968. }
  13969. return $matches;
  13970. }
  13971. private function contextNone()
  13972. {
  13973. $matches = $this->match('~
  13974. '.$this->macroRe.' ## curly tag
  13975. ~xsi');
  13976. return $matches;
  13977. }
  13978. private function match($re)
  13979. {
  13980. if ($matches = Strings::match($this->input, $re, PREG_OFFSET_CAPTURE, $this->offset)) {
  13981. $this->output .= substr($this->input, $this->offset, $matches[0][1] - $this->offset);
  13982. $this->offset = $matches[0][1] + strlen($matches[0][0]);
  13983. foreach ($matches as $k => $v) $matches[$k] = $v[0];
  13984. }
  13985. return $matches;
  13986. }
  13987. function getLine()
  13988. {
  13989. return $this->input && $this->offset ? substr_count($this->input, "\n", 0, $this->offset - 1) + 1 : NULL;
  13990. }
  13991. function setDelimiters($left, $right)
  13992. {
  13993. $this->macroRe = '
  13994. (?P<comment>' . $left . '\\*.*?\\*' . $right . '\n{0,2})|
  13995. ' . $left . '
  13996. (?P<macro>(?:' . self::RE_STRING . '|[^\'"]+?)*?)
  13997. ' . $right . '
  13998. (?P<rmargin>[ \t]*(?=\n))?
  13999. ';
  14000. return $this;
  14001. }
  14002. function writeMacro($name, $args = NULL, $modifiers = NULL, $isRightmost = FALSE)
  14003. {
  14004. $isLeftmost = trim(substr($this->output, $leftOfs = strrpos("\n$this->output", "\n"))) === '';
  14005. if ($name[0] === '/') {
  14006. $node = end($this->macroNodes);
  14007. if (!$node || "/$node->name" !== $name || $modifiers
  14008. || ($args && $node->args && !Strings::startsWith("$node->args ", "$args "))
  14009. ) {
  14010. $name .= $args ? ' ' : '';
  14011. throw new ParseException("Unexpected macro {{$name}{$args}{$modifiers}}"
  14012. . ($node ? ", expecting {/$node->name}" . ($args && $node->args ? " or eventually {/$node->name $node->args}" : '') : ''),
  14013. 0, $this->getLine());
  14014. }
  14015. array_pop($this->macroNodes);
  14016. if (!$node->args) {
  14017. $node->setArgs($args);
  14018. }
  14019. if ($isLeftmost && $isRightmost) {
  14020. $this->output = substr($this->output, 0, $leftOfs);
  14021. }
  14022. $code = $node->close(substr($this->output, $node->offset));
  14023. if (!$isLeftmost && $isRightmost && substr($code, -2) === '?>') {
  14024. $code .= "\n";
  14025. }
  14026. $this->output = substr($this->output, 0, $node->offset) . $node->content. $code;
  14027. } else {
  14028. list($node, $code) = $this->expandMacro($name, $args, $modifiers);
  14029. if (!$node->isEmpty) {
  14030. $this->macroNodes[] = $node;
  14031. }
  14032. if ($isRightmost) {
  14033. if ($isLeftmost && substr($code, 0, 11) !== '<?php echo ') {
  14034. $this->output = substr($this->output, 0, $leftOfs);
  14035. } elseif (substr($code, -2) === '?>') {
  14036. $code .= "\n";
  14037. }
  14038. }
  14039. $this->output .= $code;
  14040. $node->offset = strlen($this->output);
  14041. }
  14042. }
  14043. function writeAttrsMacro($code, $attrs, $closing)
  14044. {
  14045. $left = $right = array();
  14046. foreach ($this->macros as $name => $foo) {
  14047. if ($name[0] === '@') {
  14048. $name = substr($name, 1);
  14049. if (isset($attrs[$name])) {
  14050. if (!$closing) {
  14051. $pos = strrpos($code, '>');
  14052. if ($code[$pos-1] === '/') {
  14053. $pos--;
  14054. }
  14055. list(, $macroCode) = $this->expandMacro("@$name", $attrs[$name]);
  14056. $code = substr_replace($code, $macroCode, $pos, 0);
  14057. }
  14058. unset($attrs[$name]);
  14059. }
  14060. }
  14061. $macro = $closing ? "/$name" : $name;
  14062. if (isset($attrs[$name])) {
  14063. if ($closing) {
  14064. $right[] = array($macro, '');
  14065. } else {
  14066. array_unshift($left, array($macro, $attrs[$name]));
  14067. }
  14068. }
  14069. $innerName = "inner-$name";
  14070. if (isset($attrs[$innerName])) {
  14071. if ($closing) {
  14072. $left[] = array($macro, '');
  14073. } else {
  14074. array_unshift($right, array($macro, $attrs[$innerName]));
  14075. }
  14076. }
  14077. $tagName = "tag-$name";
  14078. if (isset($attrs[$tagName])) {
  14079. array_unshift($left, array($name, $attrs[$tagName]));
  14080. $right[] = array("/$name", '');
  14081. }
  14082. unset($attrs[$name], $attrs[$innerName], $attrs[$tagName]);
  14083. }
  14084. if ($attrs) {
  14085. throw new ParseException("Unknown macro-attribute " . self::N_PREFIX
  14086. . implode(' and ' . self::N_PREFIX, array_keys($attrs)), 0, $this->getLine());
  14087. }
  14088. foreach ($left as $item) {
  14089. $this->writeMacro($item[0], $item[1]);
  14090. if (substr($this->output, -2) === '?>') {
  14091. $this->output .= "\n";
  14092. }
  14093. }
  14094. $this->output .= $code;
  14095. foreach ($right as $item) {
  14096. $this->writeMacro($item[0], $item[1]);
  14097. if (substr($this->output, -2) === '?>') {
  14098. $this->output .= "\n";
  14099. }
  14100. }
  14101. }
  14102. function expandMacro($name, $args, $modifiers = NULL)
  14103. {
  14104. if (empty($this->macros[$name])) {
  14105. throw new ParseException("Unknown macro {{$name}}", 0, $this->getLine());
  14106. }
  14107. foreach (array_reverse($this->macros[$name]) as $macro) {
  14108. $node = new MacroNode($macro, $name, $args, $modifiers, $this->macroNodes ? end($this->macroNodes) : NULL);
  14109. $code = $macro->nodeOpened($node);
  14110. if ($code !== FALSE) {
  14111. return array($node, $code);
  14112. }
  14113. }
  14114. throw new ParseException("Unhandled macro {{$name}}", 0, $this->getLine());
  14115. }
  14116. function parseMacro($macro)
  14117. {
  14118. $match = Strings::match($macro, '~^
  14119. (
  14120. (?P<name>\?|/?[a-z]++(?:[.:][a-z0-9]+)*+(?!::|\())| ## ?, name, /name, but not function( or class::
  14121. (?P<noescape>!?)(?P<shortname>[=\~#%^&_]?) ## [!] [=] expression to print
  14122. )(?P<args>.*?)
  14123. (?P<modifiers>\|[a-z](?:'.Parser::RE_STRING.'|[^\'"]+)*)?
  14124. ()$~isx');
  14125. if (!$match) {
  14126. return FALSE;
  14127. }
  14128. if ($match['name'] === '') {
  14129. $match['name'] = $match['shortname'] ?: '=';
  14130. if (!$match['noescape']) {
  14131. $match['modifiers'] .= '|escape';
  14132. }
  14133. }
  14134. return array($match['name'], trim($match['args']), $match['modifiers']);
  14135. }
  14136. }
  14137. class PhpWriter extends Nette\Object
  14138. {
  14139. private $argsTokenizer;
  14140. private $modifiers;
  14141. private $context;
  14142. static function using(MacroNode $node, $context = NULL)
  14143. {
  14144. return new static($node->tokenizer, $node->modifiers, $context);
  14145. }
  14146. function __construct(MacroTokenizer $argsTokenizer, $modifiers = NULL, $context = NULL)
  14147. {
  14148. $this->argsTokenizer = $argsTokenizer;
  14149. $this->modifiers = $modifiers;
  14150. $this->context = $context;
  14151. }
  14152. function write($mask)
  14153. {
  14154. $args = func_get_args();
  14155. array_shift($args);
  14156. $word = strpos($mask, '%node.word') === FALSE ? NULL : $this->argsTokenizer->fetchWord();
  14157. $me = $this;
  14158. $mask = Nette\Utils\Strings::replace($mask, '#%escape\(((?>[^()]+)|\((?1)\))*\)#', function($m) use($me) {
  14159. return $me->escape(substr($m[0], 8, -1));
  14160. });
  14161. return Nette\Utils\Strings::replace($mask, '#([,+]\s*)?%(node\.word|node\.array|node\.args|modify|var)(\?)?(\s*\+\s*)?()#',
  14162. function($m) use($me, $word, & $args) {
  14163. list(, $l, $macro, $cond, $r) = $m;
  14164. switch ($macro) {
  14165. case 'node.word':
  14166. $code = $me->formatWord($word); break;
  14167. case 'node.args':
  14168. $code = $me->formatArgs(); break;
  14169. case 'node.array':
  14170. $code = $me->formatArray();
  14171. $code = $cond && $code === 'array()' ? '' : $code; break;
  14172. case 'modify':
  14173. $code = $me->formatModifiers(array_shift($args)); break;
  14174. case 'var':
  14175. $code = var_export(array_shift($args), TRUE); break;
  14176. }
  14177. if ($cond && $code === '') {
  14178. return $r ? $l : $r;
  14179. } else {
  14180. return $l . $code . $r;
  14181. }
  14182. });
  14183. }
  14184. function formatModifiers($var)
  14185. {
  14186. $modifiers = ltrim($this->modifiers, '|');
  14187. if (!$modifiers) {
  14188. return $var;
  14189. }
  14190. $tokenizer = $this->preprocess(new MacroTokenizer($modifiers));
  14191. $inside = FALSE;
  14192. while ($token = $tokenizer->fetchToken()) {
  14193. if ($token['type'] === MacroTokenizer::T_WHITESPACE) {
  14194. $var = rtrim($var) . ' ';
  14195. } elseif (!$inside) {
  14196. if ($token['type'] === MacroTokenizer::T_SYMBOL) {
  14197. if ($this->context && $token['value'] === 'escape') {
  14198. $var = $this->escape($var);
  14199. $tokenizer->fetch('|');
  14200. } else {
  14201. $var = "\$template->" . $token['value'] . "($var";
  14202. $inside = TRUE;
  14203. }
  14204. } else {
  14205. throw new ParseException("Modifier name must be alphanumeric string, '$token[value]' given.");
  14206. }
  14207. } else {
  14208. if ($token['value'] === ':' || $token['value'] === ',') {
  14209. $var = $var . ', ';
  14210. } elseif ($token['value'] === '|') {
  14211. $var = $var . ')';
  14212. $inside = FALSE;
  14213. } else {
  14214. $var .= $this->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
  14215. }
  14216. }
  14217. }
  14218. return $inside ? "$var)" : $var;
  14219. }
  14220. function formatArgs()
  14221. {
  14222. $out = '';
  14223. $tokenizer = $this->preprocess();
  14224. while ($token = $tokenizer->fetchToken()) {
  14225. $out .= $this->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
  14226. }
  14227. return $out;
  14228. }
  14229. function formatArray()
  14230. {
  14231. $out = '';
  14232. $expand = NULL;
  14233. $tokenizer = $this->preprocess();
  14234. while ($token = $tokenizer->fetchToken()) {
  14235. if ($token['value'] === '(expand)' && $token['depth'] === 0) {
  14236. $expand = TRUE;
  14237. $out .= '),';
  14238. } elseif ($expand && ($token['value'] === ',') && !$token['depth']) {
  14239. $expand = FALSE;
  14240. $out .= ', array(';
  14241. } else {
  14242. $out .= $this->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
  14243. }
  14244. }
  14245. if ($expand === NULL) {
  14246. return "array($out)";
  14247. } else {
  14248. return "array_merge(array($out" . ($expand ? ', array(' : '') ."))";
  14249. }
  14250. }
  14251. function formatWord($s)
  14252. {
  14253. return (is_numeric($s) || strspn($s, '\'"$') || in_array(strtolower($s), array('true', 'false', 'null')))
  14254. ? $s : '"' . $s . '"';
  14255. }
  14256. function canQuote($tokenizer)
  14257. {
  14258. return $tokenizer->isCurrent(MacroTokenizer::T_SYMBOL)
  14259. && (!$tokenizer->hasPrev() || $tokenizer->isPrev(',', '(', '[', '=', '=>', ':', '?'))
  14260. && (!$tokenizer->hasNext() || $tokenizer->isNext(',', ')', ']', '=', '=>', ':', '|'));
  14261. }
  14262. function preprocess(MacroTokenizer $tokenizer = NULL)
  14263. {
  14264. $tokenizer = $tokenizer === NULL ? $this->argsTokenizer : $tokenizer;
  14265. $inTernary = $prev = NULL;
  14266. $tokens = $arrays = array();
  14267. while ($token = $tokenizer->fetchToken()) {
  14268. $token['depth'] = $depth = count($arrays);
  14269. if ($token['type'] === MacroTokenizer::T_COMMENT) {
  14270. continue;
  14271. } elseif ($token['type'] === MacroTokenizer::T_WHITESPACE) {
  14272. $tokens[] = $token;
  14273. continue;
  14274. }
  14275. if ($token['value'] === '?') {
  14276. $inTernary = $depth;
  14277. } elseif ($token['value'] === ':') {
  14278. $inTernary = NULL;
  14279. } elseif ($inTernary === $depth && ($token['value'] === ',' || $token['value'] === ')' || $token['value'] === ']')) {
  14280. $tokens[] = MacroTokenizer::createToken(':') + array('depth' => $depth);
  14281. $tokens[] = MacroTokenizer::createToken('null') + array('depth' => $depth);
  14282. $inTernary = NULL;
  14283. }
  14284. if ($token['value'] === '[') {
  14285. if ($arrays[] = $prev['value'] !== ']' && $prev['type'] !== MacroTokenizer::T_SYMBOL && $prev['type'] !== MacroTokenizer::T_VARIABLE) {
  14286. $tokens[] = MacroTokenizer::createToken('array') + array('depth' => $depth);
  14287. $token = MacroTokenizer::createToken('(');
  14288. }
  14289. } elseif ($token['value'] === ']') {
  14290. if (array_pop($arrays) === TRUE) {
  14291. $token = MacroTokenizer::createToken(')');
  14292. }
  14293. } elseif ($token['value'] === '(') {
  14294. $arrays[] = '(';
  14295. } elseif ($token['value'] === ')') {
  14296. array_pop($arrays);
  14297. }
  14298. $tokens[] = $prev = $token;
  14299. }
  14300. if ($inTernary !== NULL) {
  14301. $tokens[] = MacroTokenizer::createToken(':') + array('depth' => count($arrays));
  14302. $tokens[] = MacroTokenizer::createToken('null') + array('depth' => count($arrays));
  14303. }
  14304. $tokenizer = clone $tokenizer;
  14305. $tokenizer->position = 0;
  14306. $tokenizer->tokens = $tokens;
  14307. return $tokenizer;
  14308. }
  14309. function escape($s)
  14310. {
  14311. switch ($this->context[0]) {
  14312. case Parser::CONTEXT_TEXT:
  14313. return "Nette\\Templating\\DefaultHelpers::escapeHtml($s, ENT_NOQUOTES)";
  14314. case Parser::CONTEXT_TAG:
  14315. return "Nette\\Templating\\DefaultHelpers::escapeHtml($s)";
  14316. case Parser::CONTEXT_ATTRIBUTE:
  14317. list(, $name, $quote) = $this->context;
  14318. $quote = $quote === '"' ? '' : ', ENT_QUOTES';
  14319. if (strncasecmp($name, 'on', 2) === 0) {
  14320. return "htmlSpecialChars(Nette\\Templating\\DefaultHelpers::escapeJs($s)$quote)";
  14321. } elseif ($name === 'style') {
  14322. return "htmlSpecialChars(Nette\\Templating\\DefaultHelpers::escapeCss($s)$quote)";
  14323. } else {
  14324. return "htmlSpecialChars($s$quote)";
  14325. }
  14326. case Parser::CONTEXT_COMMENT:
  14327. return "Nette\\Templating\\DefaultHelpers::escapeHtmlComment($s)";
  14328. case Parser::CONTEXT_CDATA;
  14329. return 'Nette\Templating\DefaultHelpers::escape' . ucfirst($this->context[1]) . "($s)";
  14330. case Parser::CONTEXT_NONE:
  14331. switch (isset($this->context[1]) ? $this->context[1] : NULL) {
  14332. case 'xml':
  14333. case 'js':
  14334. case 'css':
  14335. return 'Nette\Templating\DefaultHelpers::escape' . ucfirst($this->context[1]) . "($s)";
  14336. case 'text':
  14337. return $s;
  14338. default:
  14339. return "\$template->escape($s)";
  14340. }
  14341. }
  14342. }
  14343. }
  14344. }
  14345. namespace Nette\Loaders {
  14346. use Nette;use Nette\Utils\Strings;use Nette\Caching\Cache;
  14347. class RobotLoader extends AutoLoader
  14348. {
  14349. public $scanDirs;
  14350. public $ignoreDirs = '.*, *.old, *.bak, *.tmp, temp';
  14351. public $acceptFiles = '*.php, *.php5';
  14352. public $autoRebuild = TRUE;
  14353. private $list = array();
  14354. private $files;
  14355. private $rebuilt = FALSE;
  14356. private $cacheStorage;
  14357. function __construct()
  14358. {
  14359. if (!extension_loaded('tokenizer')) {
  14360. throw new Nette\NotSupportedException("PHP extension Tokenizer is not loaded.");
  14361. }
  14362. }
  14363. function register()
  14364. {
  14365. $cache = $this->getCache();
  14366. $key = $this->getKey();
  14367. if (isset($cache[$key])) {
  14368. $this->list = $cache[$key];
  14369. } else {
  14370. $this->rebuild();
  14371. }
  14372. if (isset($this->list[strtolower(__CLASS__)]) && class_exists('Nette\Loaders\NetteLoader', FALSE)) {
  14373. NetteLoader::getInstance()->unregister();
  14374. }
  14375. parent::register();
  14376. }
  14377. function tryLoad($type)
  14378. {
  14379. $type = ltrim(strtolower($type), '\\');
  14380. if (isset($this->list[$type][0]) && !is_file($this->list[$type][0])) {
  14381. unset($this->list[$type]);
  14382. }
  14383. if (!isset($this->list[$type])) {
  14384. $trace = debug_backtrace();
  14385. $initiator = & $trace[2]['function'];
  14386. if ($initiator === 'class_exists' || $initiator === 'interface_exists') {
  14387. $this->list[$type] = FALSE;
  14388. if ($this->autoRebuild && $this->rebuilt) {
  14389. $this->getCache()->save($this->getKey(), $this->list, array(
  14390. Cache::CONSTS => 'Nette\Framework::REVISION',
  14391. ));
  14392. }
  14393. }
  14394. if ($this->autoRebuild && !$this->rebuilt) {
  14395. $this->rebuild();
  14396. }
  14397. }
  14398. if (isset($this->list[$type][0])) {
  14399. Nette\Utils\LimitedScope::load($this->list[$type][0]);
  14400. self::$count++;
  14401. }
  14402. }
  14403. function rebuild()
  14404. {
  14405. $this->getCache()->save($this->getKey(), callback($this, '_rebuildCallback'), array(
  14406. Cache::CONSTS => 'Nette\Framework::REVISION',
  14407. ));
  14408. $this->rebuilt = TRUE;
  14409. }
  14410. function _rebuildCallback()
  14411. {
  14412. foreach ($this->list as $pair) {
  14413. if ($pair) {
  14414. $this->files[$pair[0]] = $pair[1];
  14415. }
  14416. }
  14417. foreach (array_unique($this->scanDirs) as $dir) {
  14418. $this->scanDirectory($dir);
  14419. }
  14420. $this->files = NULL;
  14421. return $this->list;
  14422. }
  14423. function getIndexedClasses()
  14424. {
  14425. $res = array();
  14426. foreach ($this->list as $class => $pair) {
  14427. if ($pair) {
  14428. $res[$pair[2]] = $pair[0];
  14429. }
  14430. }
  14431. return $res;
  14432. }
  14433. function addDirectory($path)
  14434. {
  14435. foreach ((array) $path as $val) {
  14436. $real = realpath($val);
  14437. if ($real === FALSE) {
  14438. throw new Nette\DirectoryNotFoundException("Directory '$val' not found.");
  14439. }
  14440. $this->scanDirs[] = $real;
  14441. }
  14442. return $this;
  14443. }
  14444. private function addClass($class, $file, $time)
  14445. {
  14446. $lClass = strtolower($class);
  14447. if (isset($this->list[$lClass][0]) && ($file2 = $this->list[$lClass][0]) !== $file && is_file($file2)) {
  14448. if ($this->files[$file2] !== filemtime($file2)) {
  14449. $this->scanScript($file2);
  14450. return $this->addClass($class, $file, $time);
  14451. }
  14452. $e = new Nette\InvalidStateException("Ambiguous class '$class' resolution; defined in $file and in " . $this->list[$lClass][0] . ".");
  14453. {
  14454. throw $e;
  14455. }
  14456. }
  14457. $this->list[$lClass] = array($file, $time, $class);
  14458. $this->files[$file] = $time;
  14459. }
  14460. private function scanDirectory($dir)
  14461. {
  14462. if (is_dir($dir)) {
  14463. $ignoreDirs = is_array($this->ignoreDirs) ? $this->ignoreDirs : Strings::split($this->ignoreDirs, '#[,\s]+#');
  14464. $disallow = array();
  14465. foreach ($ignoreDirs as $item) {
  14466. if ($item = realpath($item)) {
  14467. $disallow[$item] = TRUE;
  14468. }
  14469. }
  14470. $iterator = Nette\Utils\Finder::findFiles(is_array($this->acceptFiles) ? $this->acceptFiles : Strings::split($this->acceptFiles, '#[,\s]+#'))
  14471. ->filter(function($file) use(&$disallow){
  14472. return !isset($disallow[$file->getPathname()]);
  14473. })
  14474. ->from($dir)
  14475. ->exclude($ignoreDirs)
  14476. ->filter($filter = function($dir) use(&$disallow){
  14477. $path = $dir->getPathname();
  14478. if (is_file("$path/netterobots.txt")) {
  14479. foreach (file("$path/netterobots.txt") as $s) {
  14480. if ($matches = Strings::match($s, '#^disallow\\s*:\\s*(\\S+)#i')) {
  14481. $disallow[$path . str_replace('/', DIRECTORY_SEPARATOR, rtrim('/' . ltrim($matches[1], '/'), '/'))] = TRUE;
  14482. }
  14483. }
  14484. }
  14485. return !isset($disallow[$path]);
  14486. });
  14487. $filter(new \SplFileInfo($dir));
  14488. } else {
  14489. $iterator = new \ArrayIterator(array(new \SplFileInfo($dir)));
  14490. }
  14491. foreach ($iterator as $entry) {
  14492. $path = $entry->getPathname();
  14493. if (!isset($this->files[$path]) || $this->files[$path] !== $entry->getMTime()) {
  14494. $this->scanScript($path);
  14495. }
  14496. }
  14497. }
  14498. private function scanScript($file)
  14499. {
  14500. $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
  14501. $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
  14502. $expected = FALSE;
  14503. $namespace = '';
  14504. $level = $minLevel = 0;
  14505. $time = filemtime($file);
  14506. $s = file_get_contents($file);
  14507. foreach ($this->list as $class => $pair) {
  14508. if ($pair && $pair[0] === $file) {
  14509. unset($this->list[$class]);
  14510. }
  14511. }
  14512. if ($matches = Strings::match($s, '#//nette'.'loader=(\S*)#')) {
  14513. foreach (explode(',', $matches[1]) as $name) {
  14514. $this->addClass($name, $file, $time);
  14515. }
  14516. return;
  14517. }
  14518. foreach (token_get_all($s) as $token) {
  14519. if (is_array($token)) {
  14520. switch ($token[0]) {
  14521. case T_COMMENT:
  14522. case T_DOC_COMMENT:
  14523. case T_WHITESPACE:
  14524. continue 2;
  14525. case $T_NS_SEPARATOR:
  14526. case T_STRING:
  14527. if ($expected) {
  14528. $name .= $token[1];
  14529. }
  14530. continue 2;
  14531. case $T_NAMESPACE:
  14532. case T_CLASS:
  14533. case T_INTERFACE:
  14534. $expected = $token[0];
  14535. $name = '';
  14536. continue 2;
  14537. case T_CURLY_OPEN:
  14538. case T_DOLLAR_OPEN_CURLY_BRACES:
  14539. $level++;
  14540. }
  14541. }
  14542. if ($expected) {
  14543. switch ($expected) {
  14544. case T_CLASS:
  14545. case T_INTERFACE:
  14546. if ($level === $minLevel) {
  14547. $this->addClass($namespace . $name, $file, $time);
  14548. }
  14549. break;
  14550. case $T_NAMESPACE:
  14551. $namespace = $name ? $name . '\\' : '';
  14552. $minLevel = $token === '{' ? 1 : 0;
  14553. }
  14554. $expected = NULL;
  14555. }
  14556. if ($token === '{') {
  14557. $level++;
  14558. } elseif ($token === '}') {
  14559. $level--;
  14560. }
  14561. }
  14562. }
  14563. function setCacheStorage(Nette\Caching\IStorage $storage)
  14564. {
  14565. $this->cacheStorage = $storage;
  14566. return $this;
  14567. }
  14568. function getCacheStorage()
  14569. {
  14570. return $this->cacheStorage;
  14571. }
  14572. protected function getCache()
  14573. {
  14574. if (!$this->cacheStorage) {
  14575. trigger_error('Missing cache storage.', E_USER_WARNING);
  14576. $this->cacheStorage = new Nette\Caching\Storages\DevNullStorage;
  14577. }
  14578. return new Cache($this->cacheStorage, 'Nette.RobotLoader');
  14579. }
  14580. protected function getKey()
  14581. {
  14582. return array($this->ignoreDirs, $this->acceptFiles, $this->scanDirs);
  14583. }
  14584. }
  14585. }
  14586. namespace Nette\Mail {
  14587. use Nette;use Nette\Utils\Strings;
  14588. class MimePart extends Nette\Object
  14589. {
  14590. const ENCODING_BASE64 = 'base64',
  14591. ENCODING_7BIT = '7bit',
  14592. ENCODING_8BIT = '8bit',
  14593. ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
  14594. const EOL = "\r\n";
  14595. const LINE_LENGTH = 76;
  14596. private $headers = array();
  14597. private $parts = array();
  14598. private $body;
  14599. function setHeader($name, $value, $append = FALSE)
  14600. {
  14601. if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
  14602. throw new Nette\InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
  14603. }
  14604. if ($value == NULL) {
  14605. if (!$append) {
  14606. unset($this->headers[$name]);
  14607. }
  14608. } elseif (is_array($value)) {
  14609. $tmp = & $this->headers[$name];
  14610. if (!$append || !is_array($tmp)) {
  14611. $tmp = array();
  14612. }
  14613. foreach ($value as $email => $name) {
  14614. if ($name !== NULL && !Strings::checkEncoding($name)) {
  14615. throw new Nette\InvalidArgumentException("Name is not valid UTF-8 string.");
  14616. }
  14617. if (!preg_match('#^[^@",\s]+@[^@",\s]+\.[a-z]{2,10}$#i', $email)) {
  14618. throw new Nette\InvalidArgumentException("Email address '$email' is not valid.");
  14619. }
  14620. if (preg_match('#[\r\n]#', $name)) {
  14621. throw new Nette\InvalidArgumentException("Name must not contain line separator.");
  14622. }
  14623. $tmp[$email] = $name;
  14624. }
  14625. } else {
  14626. $value = (string) $value;
  14627. if (!Strings::checkEncoding($value)) {
  14628. throw new Nette\InvalidArgumentException("Header is not valid UTF-8 string.");
  14629. }
  14630. $this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
  14631. }
  14632. return $this;
  14633. }
  14634. function getHeader($name)
  14635. {
  14636. return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
  14637. }
  14638. function clearHeader($name)
  14639. {
  14640. unset($this->headers[$name]);
  14641. return $this;
  14642. }
  14643. function getEncodedHeader($name)
  14644. {
  14645. $offset = strlen($name) + 2;
  14646. if (!isset($this->headers[$name])) {
  14647. return NULL;
  14648. } elseif (is_array($this->headers[$name])) {
  14649. $s = '';
  14650. foreach ($this->headers[$name] as $email => $name) {
  14651. if ($name != NULL) {
  14652. $s .= self::encodeHeader(
  14653. strpbrk($name, '.,;<@>()[]"=?') ? '"' . addcslashes($name, '"\\') . '"' : $name,
  14654. $offset
  14655. );
  14656. $email = " <$email>";
  14657. }
  14658. $email .= ',';
  14659. if ($s !== '' && $offset + strlen($email) > self::LINE_LENGTH) {
  14660. $s .= self::EOL . "\t";
  14661. $offset = 1;
  14662. }
  14663. $s .= $email;
  14664. $offset += strlen($email);
  14665. }
  14666. return substr($s, 0, -1);
  14667. } else {
  14668. return self::encodeHeader($this->headers[$name], $offset);
  14669. }
  14670. }
  14671. function getHeaders()
  14672. {
  14673. return $this->headers;
  14674. }
  14675. function setContentType($contentType, $charset = NULL)
  14676. {
  14677. $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
  14678. return $this;
  14679. }
  14680. function setEncoding($encoding)
  14681. {
  14682. $this->setHeader('Content-Transfer-Encoding', $encoding);
  14683. return $this;
  14684. }
  14685. function getEncoding()
  14686. {
  14687. return $this->getHeader('Content-Transfer-Encoding');
  14688. }
  14689. function addPart(MimePart $part = NULL)
  14690. {
  14691. return $this->parts[] = $part === NULL ? new self : $part;
  14692. }
  14693. function setBody($body)
  14694. {
  14695. $this->body = $body;
  14696. return $this;
  14697. }
  14698. function getBody()
  14699. {
  14700. return $this->body;
  14701. }
  14702. function generateMessage()
  14703. {
  14704. $output = '';
  14705. $boundary = '--------' . Strings::random();
  14706. foreach ($this->headers as $name => $value) {
  14707. $output .= $name . ': ' . $this->getEncodedHeader($name);
  14708. if ($this->parts && $name === 'Content-Type') {
  14709. $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
  14710. }
  14711. $output .= self::EOL;
  14712. }
  14713. $output .= self::EOL;
  14714. $body = (string) $this->body;
  14715. if ($body !== '') {
  14716. switch ($this->getEncoding()) {
  14717. case self::ENCODING_QUOTED_PRINTABLE:
  14718. $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
  14719. break;
  14720. case self::ENCODING_BASE64:
  14721. $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
  14722. break;
  14723. case self::ENCODING_7BIT:
  14724. $body = preg_replace('#[\x80-\xFF]+#', '', $body);
  14725. case self::ENCODING_8BIT:
  14726. $body = str_replace(array("\x00", "\r"), '', $body);
  14727. $body = str_replace("\n", self::EOL, $body);
  14728. $output .= $body;
  14729. break;
  14730. default:
  14731. throw new Nette\InvalidStateException('Unknown encoding.');
  14732. }
  14733. }
  14734. if ($this->parts) {
  14735. if (substr($output, -strlen(self::EOL)) !== self::EOL) {
  14736. $output .= self::EOL;
  14737. }
  14738. foreach ($this->parts as $part) {
  14739. $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
  14740. }
  14741. $output .= '--' . $boundary.'--';
  14742. }
  14743. return $output;
  14744. }
  14745. private static function encodeHeader($s, & $offset = 0)
  14746. {
  14747. $o = '';
  14748. if ($offset >= 55) {
  14749. $o = self::EOL . "\t";
  14750. $offset = 1;
  14751. }
  14752. if (strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}=? _\r\n\t") === strlen($s)
  14753. && ($offset + strlen($s) <= self::LINE_LENGTH)
  14754. ) {
  14755. $offset += strlen($s);
  14756. return $o . $s;
  14757. }
  14758. $o .= str_replace("\n ", "\n\t", substr(iconv_mime_encode(str_repeat(' ', $offset), $s, array(
  14759. 'scheme' => 'B',
  14760. 'input-charset' => 'UTF-8',
  14761. 'output-charset' => 'UTF-8',
  14762. )), $offset + 2));
  14763. $offset = strlen($o) - strrpos($o, "\n");
  14764. return $o;
  14765. }
  14766. }
  14767. class Message extends MimePart
  14768. {
  14769. const HIGH = 1,
  14770. NORMAL = 3,
  14771. LOW = 5;
  14772. public static $defaultMailer = 'Nette\Mail\SendmailMailer';
  14773. public static $defaultHeaders = array(
  14774. 'MIME-Version' => '1.0',
  14775. 'X-Mailer' => 'Nette Framework',
  14776. );
  14777. private $mailer;
  14778. private $attachments = array();
  14779. private $inlines = array();
  14780. private $html;
  14781. private $basePath;
  14782. function __construct()
  14783. {
  14784. foreach (self::$defaultHeaders as $name => $value) {
  14785. $this->setHeader($name, $value);
  14786. }
  14787. $this->setHeader('Date', date('r'));
  14788. }
  14789. function setFrom($email, $name = NULL)
  14790. {
  14791. $this->setHeader('From', $this->formatEmail($email, $name));
  14792. return $this;
  14793. }
  14794. function getFrom()
  14795. {
  14796. return $this->getHeader('From');
  14797. }
  14798. function addReplyTo($email, $name = NULL)
  14799. {
  14800. $this->setHeader('Reply-To', $this->formatEmail($email, $name), TRUE);
  14801. return $this;
  14802. }
  14803. function setSubject($subject)
  14804. {
  14805. $this->setHeader('Subject', $subject);
  14806. return $this;
  14807. }
  14808. function getSubject()
  14809. {
  14810. return $this->getHeader('Subject');
  14811. }
  14812. function addTo($email, $name = NULL)
  14813. {
  14814. $this->setHeader('To', $this->formatEmail($email, $name), TRUE);
  14815. return $this;
  14816. }
  14817. function addCc($email, $name = NULL)
  14818. {
  14819. $this->setHeader('Cc', $this->formatEmail($email, $name), TRUE);
  14820. return $this;
  14821. }
  14822. function addBcc($email, $name = NULL)
  14823. {
  14824. $this->setHeader('Bcc', $this->formatEmail($email, $name), TRUE);
  14825. return $this;
  14826. }
  14827. private function formatEmail($email, $name)
  14828. {
  14829. if (!$name && preg_match('#^(.+) +<(.*)>$#', $email, $matches)) {
  14830. return array($matches[2] => $matches[1]);
  14831. } else {
  14832. return array($email => $name);
  14833. }
  14834. }
  14835. function setReturnPath($email)
  14836. {
  14837. $this->setHeader('Return-Path', $email);
  14838. return $this;
  14839. }
  14840. function getReturnPath()
  14841. {
  14842. return $this->getHeader('From');
  14843. }
  14844. function setPriority($priority)
  14845. {
  14846. $this->setHeader('X-Priority', (int) $priority);
  14847. return $this;
  14848. }
  14849. function getPriority()
  14850. {
  14851. return $this->getHeader('X-Priority');
  14852. }
  14853. function setHtmlBody($html, $basePath = NULL)
  14854. {
  14855. $this->html = $html;
  14856. $this->basePath = $basePath;
  14857. return $this;
  14858. }
  14859. function getHtmlBody()
  14860. {
  14861. return $this->html;
  14862. }
  14863. function addEmbeddedFile($file, $content = NULL, $contentType = NULL)
  14864. {
  14865. return $this->inlines[$file] = $this->createAttachment($file, $content, $contentType, 'inline')
  14866. ->setHeader('Content-ID', $this->getRandomId());
  14867. }
  14868. function addAttachment($file, $content = NULL, $contentType = NULL)
  14869. {
  14870. return $this->attachments[] = $this->createAttachment($file, $content, $contentType, 'attachment');
  14871. }
  14872. private function createAttachment($file, $content, $contentType, $disposition)
  14873. {
  14874. $part = new MimePart;
  14875. if ($content === NULL) {
  14876. $content = file_get_contents($file);
  14877. if ($content === FALSE) {
  14878. throw new Nette\FileNotFoundException("Unable to read file '$file'.");
  14879. }
  14880. } else {
  14881. $content = (string) $content;
  14882. }
  14883. $part->setBody($content);
  14884. $part->setContentType($contentType ? $contentType : Nette\Utils\MimeTypeDetector::fromString($content));
  14885. $part->setEncoding(preg_match('#(multipart|message)/#A', $contentType) ? self::ENCODING_8BIT : self::ENCODING_BASE64);
  14886. $part->setHeader('Content-Disposition', $disposition . '; filename="' . Strings::fixEncoding(basename($file)) . '"');
  14887. return $part;
  14888. }
  14889. function send()
  14890. {
  14891. $this->getMailer()->send($this->build());
  14892. }
  14893. function setMailer(IMailer $mailer)
  14894. {
  14895. $this->mailer = $mailer;
  14896. return $this;
  14897. }
  14898. function getMailer()
  14899. {
  14900. if ($this->mailer === NULL) {
  14901. $this->mailer = is_object(self::$defaultMailer) ? self::$defaultMailer : new static::$defaultMailer;
  14902. }
  14903. return $this->mailer;
  14904. }
  14905. function generateMessage()
  14906. {
  14907. if ($this->getHeader('Message-ID')) {
  14908. return parent::generateMessage();
  14909. } else {
  14910. return $this->build()->generateMessage();
  14911. }
  14912. }
  14913. protected function build()
  14914. {
  14915. $mail = clone $this;
  14916. $mail->setHeader('Message-ID', $this->getRandomId());
  14917. $mail->buildHtml();
  14918. $mail->buildText();
  14919. $cursor = $mail;
  14920. if ($mail->attachments) {
  14921. $tmp = $cursor->setContentType('multipart/mixed');
  14922. $cursor = $cursor->addPart();
  14923. foreach ($mail->attachments as $value) {
  14924. $tmp->addPart($value);
  14925. }
  14926. }
  14927. if ($mail->html != NULL) {
  14928. $tmp = $cursor->setContentType('multipart/alternative');
  14929. $cursor = $cursor->addPart();
  14930. $alt = $tmp->addPart();
  14931. if ($mail->inlines) {
  14932. $tmp = $alt->setContentType('multipart/related');
  14933. $alt = $alt->addPart();
  14934. foreach ($mail->inlines as $name => $value) {
  14935. $tmp->addPart($value);
  14936. }
  14937. }
  14938. $alt->setContentType('text/html', 'UTF-8')
  14939. ->setEncoding(preg_match('#[\x80-\xFF]#', $mail->html) ? self::ENCODING_8BIT : self::ENCODING_7BIT)
  14940. ->setBody($mail->html);
  14941. }
  14942. $text = $mail->getBody();
  14943. $mail->setBody(NULL);
  14944. $cursor->setContentType('text/plain', 'UTF-8')
  14945. ->setEncoding(preg_match('#[\x80-\xFF]#', $text) ? self::ENCODING_8BIT : self::ENCODING_7BIT)
  14946. ->setBody($text);
  14947. return $mail;
  14948. }
  14949. protected function buildHtml()
  14950. {
  14951. if ($this->html instanceof Nette\Templating\ITemplate) {
  14952. $this->html->mail = $this;
  14953. if ($this->basePath === NULL && $this->html instanceof Nette\Templating\IFileTemplate) {
  14954. $this->basePath = dirname($this->html->getFile());
  14955. }
  14956. $this->html = $this->html->__toString(TRUE);
  14957. }
  14958. if ($this->basePath !== FALSE) {
  14959. $cids = array();
  14960. $matches = Strings::matchAll(
  14961. $this->html,
  14962. '#(src\s*=\s*|background\s*=\s*|url\()(["\'])(?![a-z]+:|[/\\#])(.+?)\\2#i',
  14963. PREG_OFFSET_CAPTURE
  14964. );
  14965. foreach (array_reverse($matches) as $m) {
  14966. $file = rtrim($this->basePath, '/\\') . '/' . $m[3][0];
  14967. if (!isset($cids[$file])) {
  14968. $cids[$file] = substr($this->addEmbeddedFile($file)->getHeader("Content-ID"), 1, -1);
  14969. }
  14970. $this->html = substr_replace($this->html,
  14971. "{$m[1][0]}{$m[2][0]}cid:{$cids[$file]}{$m[2][0]}",
  14972. $m[0][1], strlen($m[0][0])
  14973. );
  14974. }
  14975. }
  14976. if (!$this->getSubject() && $matches = Strings::match($this->html, '#<title>(.+?)</title>#is')) {
  14977. $this->setSubject(html_entity_decode($matches[1], ENT_QUOTES, 'UTF-8'));
  14978. }
  14979. }
  14980. protected function buildText()
  14981. {
  14982. $text = $this->getBody();
  14983. if ($text instanceof Nette\Templating\ITemplate) {
  14984. $text->mail = $this;
  14985. $this->setBody($text->__toString(TRUE));
  14986. } elseif ($text == NULL && $this->html != NULL) {
  14987. $text = Strings::replace($this->html, array(
  14988. '#<(style|script|head).*</\\1>#Uis' => '',
  14989. '#<t[dh][ >]#i' => " $0",
  14990. '#[ \t\r\n]+#' => ' ',
  14991. '#<(/?p|/?h\d|li|br|/tr)[ >/]#i' => "\n$0",
  14992. ));
  14993. $text = html_entity_decode(strip_tags($text), ENT_QUOTES, 'UTF-8');
  14994. $this->setBody(trim($text));
  14995. }
  14996. }
  14997. private function getRandomId()
  14998. {
  14999. return '<' . Strings::random() . '@' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
  15000. : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost'))
  15001. . '>';
  15002. }
  15003. }
  15004. class SendmailMailer extends Nette\Object implements IMailer
  15005. {
  15006. function send(Message $mail)
  15007. {
  15008. $tmp = clone $mail;
  15009. $tmp->setHeader('Subject', NULL);
  15010. $tmp->setHeader('To', NULL);
  15011. $parts = explode(Message::EOL . Message::EOL, $tmp->generateMessage(), 2);
  15012. Nette\Diagnostics\Debugger::tryError();
  15013. $res = mail(
  15014. str_replace(Message::EOL, PHP_EOL, $mail->getEncodedHeader('To')),
  15015. str_replace(Message::EOL, PHP_EOL, $mail->getEncodedHeader('Subject')),
  15016. str_replace(Message::EOL, PHP_EOL, $parts[1]),
  15017. str_replace(Message::EOL, PHP_EOL, $parts[0])
  15018. );
  15019. if (Nette\Diagnostics\Debugger::catchError($e)) {
  15020. throw new Nette\InvalidStateException('mail(): ' . $e->getMessage(), 0, $e);
  15021. } elseif (!$res) {
  15022. throw new Nette\InvalidStateException('Unable to send email.');
  15023. }
  15024. }
  15025. }
  15026. class SmtpMailer extends Nette\Object implements IMailer
  15027. {
  15028. private $connection;
  15029. private $host;
  15030. private $port;
  15031. private $username;
  15032. private $password;
  15033. private $secure;
  15034. private $timeout;
  15035. function __construct(array $options = array())
  15036. {
  15037. if (isset($options['host'])) {
  15038. $this->host = $options['host'];
  15039. $this->port = isset($options['port']) ? (int) $options['port'] : NULL;
  15040. } else {
  15041. $this->host = ini_get('SMTP');
  15042. $this->port = (int) ini_get('smtp_port');
  15043. }
  15044. $this->username = isset($options['username']) ? $options['username'] : '';
  15045. $this->password = isset($options['password']) ? $options['password'] : '';
  15046. $this->secure = isset($options['secure']) ? $options['secure'] : '';
  15047. $this->timeout = isset($options['timeout']) ? (int) $options['timeout'] : 20;
  15048. if (!$this->port) {
  15049. $this->port = $this->secure === 'ssl' ? 465 : 25;
  15050. }
  15051. }
  15052. function send(Message $mail)
  15053. {
  15054. $data = $mail->generateMessage();
  15055. $this->connect();
  15056. $from = $mail->getHeader('From');
  15057. if ($from) {
  15058. $from = array_keys($from);
  15059. $this->write("MAIL FROM:<$from[0]>", 250);
  15060. }
  15061. foreach (array_merge(
  15062. (array) $mail->getHeader('To'),
  15063. (array) $mail->getHeader('Cc'),
  15064. (array) $mail->getHeader('Bcc')
  15065. ) as $email => $name) {
  15066. $this->write("RCPT TO:<$email>", array(250, 251));
  15067. }
  15068. $this->write('DATA', 354);
  15069. $data = preg_replace('#^\.#m', '..', $data);
  15070. $this->write($data);
  15071. $this->write('.', 250);
  15072. $this->write('QUIT', 221);
  15073. $this->disconnect();
  15074. }
  15075. private function connect()
  15076. {
  15077. $this->connection = @fsockopen(
  15078. ($this->secure === 'ssl' ? 'ssl://' : '') . $this->host,
  15079. $this->port, $errno, $error, $this->timeout
  15080. );
  15081. if (!$this->connection) {
  15082. throw new SmtpException($error, $errno);
  15083. }
  15084. stream_set_timeout($this->connection, $this->timeout, 0);
  15085. $this->read();
  15086. $self = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost';
  15087. $this->write("EHLO $self");
  15088. if ((int) $this->read() !== 250) {
  15089. $this->write("HELO $self", 250);
  15090. }
  15091. if ($this->secure === 'tls') {
  15092. $this->write('STARTTLS', 220);
  15093. if (!stream_socket_enable_crypto($this->connection, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
  15094. throw new SmtpException('Unable to connect via TLS.');
  15095. }
  15096. }
  15097. if ($this->username != NULL && $this->password != NULL) {
  15098. $this->write('AUTH LOGIN', 334);
  15099. $this->write(base64_encode($this->username), 334, 'username');
  15100. $this->write(base64_encode($this->password), 235, 'password');
  15101. }
  15102. }
  15103. private function disconnect()
  15104. {
  15105. fclose($this->connection);
  15106. $this->connection = NULL;
  15107. }
  15108. private function write($line, $expectedCode = NULL, $message = NULL)
  15109. {
  15110. fwrite($this->connection, $line . Message::EOL);
  15111. if ($expectedCode && !in_array((int) $this->read(), (array) $expectedCode)) {
  15112. throw new SmtpException('SMTP server did not accept ' . ($message ? $message : $line));
  15113. }
  15114. }
  15115. private function read()
  15116. {
  15117. $s = '';
  15118. while (($line = fgets($this->connection, 1e3)) != NULL) {
  15119. $s .= $line;
  15120. if (substr($line, 3, 1) === ' ') {
  15121. break;
  15122. }
  15123. }
  15124. return $s;
  15125. }
  15126. }
  15127. class SmtpException extends \Exception
  15128. {
  15129. }
  15130. }
  15131. namespace Nette\Reflection {
  15132. use Nette;
  15133. class Annotation extends Nette\Object implements IAnnotation
  15134. {
  15135. function __construct(array $values)
  15136. {
  15137. foreach ($values as $k => $v) {
  15138. $this->$k = $v;
  15139. }
  15140. }
  15141. function __toString()
  15142. {
  15143. return $this->value;
  15144. }
  15145. }
  15146. use Nette\Utils\Strings;
  15147. /**
  15148. * Annotations support for PHP.
  15149. *
  15150. * @author David Grudl
  15151. * @Annotation
  15152. */
  15153. final class AnnotationsParser
  15154. {
  15155. const RE_STRING = '\'(?:\\\\.|[^\'\\\\])*\'|"(?:\\\\.|[^"\\\\])*"';
  15156. const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF-]*';
  15157. public static $useReflection;
  15158. public static $inherited = array('description', 'param', 'return');
  15159. private static $cache;
  15160. private static $timestamps;
  15161. private static $cacheStorage;
  15162. final function __construct()
  15163. {
  15164. throw new Nette\StaticClassException;
  15165. }
  15166. static function getAll(\Reflector $r)
  15167. {
  15168. if ($r instanceof \ReflectionClass) {
  15169. $type = $r->getName();
  15170. $member = '';
  15171. } elseif ($r instanceof \ReflectionMethod) {
  15172. $type = $r->getDeclaringClass()->getName();
  15173. $member = $r->getName();
  15174. } else {
  15175. $type = $r->getDeclaringClass()->getName();
  15176. $member = '$' . $r->getName();
  15177. }
  15178. if (!self::$useReflection) {
  15179. $file = $r instanceof \ReflectionClass ? $r->getFileName() : $r->getDeclaringClass()->getFileName();
  15180. if ($file && isset(self::$timestamps[$file]) && self::$timestamps[$file] !== filemtime($file)) {
  15181. unset(self::$cache[$type]);
  15182. }
  15183. unset(self::$timestamps[$file]);
  15184. }
  15185. if (isset(self::$cache[$type][$member])) {
  15186. return self::$cache[$type][$member];
  15187. }
  15188. if (self::$useReflection === NULL) {
  15189. self::$useReflection = (bool) ClassType::from(__CLASS__)->getDocComment();
  15190. }
  15191. if (self::$useReflection) {
  15192. $annotations = self::parseComment($r->getDocComment());
  15193. } else {
  15194. if (!self::$cacheStorage) {
  15195. self::$cacheStorage = new Nette\Caching\Storages\DevNullStorage;
  15196. }
  15197. $outerCache = new Nette\Caching\Cache(self::$cacheStorage, 'Nette.Reflection.Annotations');
  15198. if (self::$cache === NULL) {
  15199. self::$cache = (array) $outerCache->offsetGet('list');
  15200. self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : array();
  15201. }
  15202. if (!isset(self::$cache[$type]) && $file) {
  15203. self::$cache['*'][$file] = filemtime($file);
  15204. self::parseScript($file);
  15205. $outerCache->save('list', self::$cache);
  15206. }
  15207. if (isset(self::$cache[$type][$member])) {
  15208. $annotations = self::$cache[$type][$member];
  15209. } else {
  15210. $annotations = array();
  15211. }
  15212. }
  15213. if ($r instanceof \ReflectionMethod && !$r->isPrivate()
  15214. && (!$r->isConstructor() || !empty($annotations['inheritdoc'][0])))
  15215. {
  15216. try {
  15217. $inherited = self::getAll(new \ReflectionMethod(get_parent_class($type), $member));
  15218. } catch (\ReflectionException $e) {
  15219. try {
  15220. $inherited = self::getAll($r->getPrototype());
  15221. } catch (\ReflectionException $e) {
  15222. $inherited = array();
  15223. }
  15224. }
  15225. $annotations += array_intersect_key($inherited, array_flip(self::$inherited));
  15226. }
  15227. return self::$cache[$type][$member] = $annotations;
  15228. }
  15229. private static function parseComment($comment)
  15230. {
  15231. static $tokens = array('true' => TRUE, 'false' => FALSE, 'null' => NULL, '' => TRUE);
  15232. $res = array();
  15233. $comment = preg_replace('#^\s*\*\s?#ms', '', trim($comment, '/*'));
  15234. $parts = preg_split('#^\s*(?=@'.self::RE_IDENTIFIER.')#m', $comment, 2);
  15235. $description = trim($parts[0]);
  15236. if ($description !== '') {
  15237. $res['description'] = array($description);
  15238. }
  15239. $matches = Strings::matchAll(
  15240. isset($parts[1]) ? $parts[1] : '',
  15241. '~
  15242. (?<=\s|^)@('.self::RE_IDENTIFIER.')[ \t]* ## annotation
  15243. (
  15244. \((?>'.self::RE_STRING.'|[^\'")@]+)+\)| ## (value)
  15245. [^(@\r\n][^@\r\n]*|) ## value
  15246. ~xi'
  15247. );
  15248. foreach ($matches as $match) {
  15249. list(, $name, $value) = $match;
  15250. if (substr($value, 0, 1) === '(') {
  15251. $items = array();
  15252. $key = '';
  15253. $val = TRUE;
  15254. $value[0] = ',';
  15255. while ($m = Strings::match(
  15256. $value,
  15257. '#\s*,\s*(?>(' . self::RE_IDENTIFIER . ')\s*=\s*)?(' . self::RE_STRING . '|[^\'"),\s][^\'"),]*)#A')
  15258. ) {
  15259. $value = substr($value, strlen($m[0]));
  15260. list(, $key, $val) = $m;
  15261. if ($val[0] === "'" || $val[0] === '"') {
  15262. $val = substr($val, 1, -1);
  15263. } elseif (is_numeric($val)) {
  15264. $val = 1 * $val;
  15265. } else {
  15266. $lval = strtolower($val);
  15267. $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val;
  15268. }
  15269. if ($key === '') {
  15270. $items[] = $val;
  15271. } else {
  15272. $items[$key] = $val;
  15273. }
  15274. }
  15275. $value = count($items) < 2 && $key === '' ? $val : $items;
  15276. } else {
  15277. $value = trim($value);
  15278. if (is_numeric($value)) {
  15279. $value = 1 * $value;
  15280. } else {
  15281. $lval = strtolower($value);
  15282. $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value;
  15283. }
  15284. }
  15285. $class = $name . 'Annotation';
  15286. if (class_exists($class)) {
  15287. $res[$name][] = new $class(is_array($value) ? $value : array('value' => $value));
  15288. } else {
  15289. $res[$name][] = is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
  15290. }
  15291. }
  15292. return $res;
  15293. }
  15294. private static function parseScript($file)
  15295. {
  15296. $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
  15297. $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
  15298. $s = file_get_contents($file);
  15299. if (Strings::match($s, '#//nette'.'loader=(\S*)#')) {
  15300. return;
  15301. }
  15302. $expected = $namespace = $class = $docComment = NULL;
  15303. $level = $classLevel = 0;
  15304. foreach (token_get_all($s) as $token) {
  15305. if (is_array($token)) {
  15306. switch ($token[0]) {
  15307. case T_DOC_COMMENT:
  15308. $docComment = $token[1];
  15309. case T_WHITESPACE:
  15310. case T_COMMENT:
  15311. continue 2;
  15312. case T_STRING:
  15313. case $T_NS_SEPARATOR:
  15314. case T_VARIABLE:
  15315. if ($expected) {
  15316. $name .= $token[1];
  15317. }
  15318. continue 2;
  15319. case T_FUNCTION:
  15320. case T_VAR:
  15321. case T_PUBLIC:
  15322. case T_PROTECTED:
  15323. case $T_NAMESPACE:
  15324. case T_CLASS:
  15325. case T_INTERFACE:
  15326. $expected = $token[0];
  15327. $name = NULL;
  15328. continue 2;
  15329. case T_STATIC:
  15330. case T_ABSTRACT:
  15331. case T_FINAL:
  15332. continue 2;
  15333. case T_CURLY_OPEN:
  15334. case T_DOLLAR_OPEN_CURLY_BRACES:
  15335. $level++;
  15336. }
  15337. }
  15338. if ($expected) {
  15339. switch ($expected) {
  15340. case T_CLASS:
  15341. case T_INTERFACE:
  15342. $class = $namespace . $name;
  15343. $classLevel = $level;
  15344. $name = '';
  15345. case T_FUNCTION:
  15346. if ($token === '&') {
  15347. continue 2;
  15348. }
  15349. case T_VAR:
  15350. case T_PUBLIC:
  15351. case T_PROTECTED:
  15352. if ($class && $name !== NULL && $docComment) {
  15353. self::$cache[$class][$name] = self::parseComment($docComment);
  15354. }
  15355. break;
  15356. case $T_NAMESPACE:
  15357. $namespace = $name . '\\';
  15358. }
  15359. $expected = $docComment = NULL;
  15360. }
  15361. if ($token === ';') {
  15362. $docComment = NULL;
  15363. } elseif ($token === '{') {
  15364. $docComment = NULL;
  15365. $level++;
  15366. } elseif ($token === '}') {
  15367. $level--;
  15368. if ($level === $classLevel) {
  15369. $class = NULL;
  15370. }
  15371. }
  15372. }
  15373. }
  15374. static function setCacheStorage(Nette\Caching\IStorage $storage)
  15375. {
  15376. self::$cacheStorage = $storage;
  15377. }
  15378. static function getCacheStorage()
  15379. {
  15380. return self::$cacheStorage;
  15381. }
  15382. }
  15383. use Nette\ObjectMixin;
  15384. class Extension extends \ReflectionExtension
  15385. {
  15386. function __toString()
  15387. {
  15388. return 'Extension ' . $this->getName();
  15389. }
  15390. function getClasses()
  15391. {
  15392. $res = array();
  15393. foreach (parent::getClassNames() as $val) {
  15394. $res[$val] = new ClassType($val);
  15395. }
  15396. return $res;
  15397. }
  15398. function getFunctions()
  15399. {
  15400. foreach ($res = parent::getFunctions() as $key => $val) {
  15401. $res[$key] = new GlobalFunction($key);
  15402. }
  15403. return $res;
  15404. }
  15405. static function getReflection()
  15406. {
  15407. return new ClassType(get_called_class());
  15408. }
  15409. function __call($name, $args)
  15410. {
  15411. return ObjectMixin::call($this, $name, $args);
  15412. }
  15413. function &__get($name)
  15414. {
  15415. return ObjectMixin::get($this, $name);
  15416. }
  15417. function __set($name, $value)
  15418. {
  15419. return ObjectMixin::set($this, $name, $value);
  15420. }
  15421. function __isset($name)
  15422. {
  15423. return ObjectMixin::has($this, $name);
  15424. }
  15425. function __unset($name)
  15426. {
  15427. ObjectMixin::remove($this, $name);
  15428. }
  15429. }
  15430. class GlobalFunction extends \ReflectionFunction
  15431. {
  15432. private $value;
  15433. function __construct($name)
  15434. {
  15435. parent::__construct($this->value = $name);
  15436. }
  15437. function getDefaultParameters()
  15438. {
  15439. return Method::buildDefaultParameters(parent::getParameters());
  15440. }
  15441. function invokeNamedArgs($args)
  15442. {
  15443. return $this->invokeArgs(Method::combineArgs($this->getDefaultParameters(), $args));
  15444. }
  15445. function toCallback()
  15446. {
  15447. return new Nette\Callback($this->value);
  15448. }
  15449. function __toString()
  15450. {
  15451. return 'Function ' . $this->getName() . '()';
  15452. }
  15453. function getClosure()
  15454. {
  15455. return $this->isClosure() ? $this->value : NULL;
  15456. }
  15457. function getExtension()
  15458. {
  15459. return ($name = $this->getExtensionName()) ? new Extension($name) : NULL;
  15460. }
  15461. function getParameters()
  15462. {
  15463. foreach ($res = parent::getParameters() as $key => $val) {
  15464. $res[$key] = new Parameter($this->value, $val->getName());
  15465. }
  15466. return $res;
  15467. }
  15468. static function getReflection()
  15469. {
  15470. return new ClassType(get_called_class());
  15471. }
  15472. function __call($name, $args)
  15473. {
  15474. return ObjectMixin::call($this, $name, $args);
  15475. }
  15476. function &__get($name)
  15477. {
  15478. return ObjectMixin::get($this, $name);
  15479. }
  15480. function __set($name, $value)
  15481. {
  15482. return ObjectMixin::set($this, $name, $value);
  15483. }
  15484. function __isset($name)
  15485. {
  15486. return ObjectMixin::has($this, $name);
  15487. }
  15488. function __unset($name)
  15489. {
  15490. ObjectMixin::remove($this, $name);
  15491. }
  15492. }
  15493. class Method extends \ReflectionMethod
  15494. {
  15495. static function from($class, $method)
  15496. {
  15497. return new static(is_object($class) ? get_class($class) : $class, $method);
  15498. }
  15499. function getDefaultParameters()
  15500. {
  15501. return self::buildDefaultParameters(parent::getParameters());
  15502. }
  15503. function invokeNamedArgs($object, $args)
  15504. {
  15505. return $this->invokeArgs($object, self::combineArgs($this->getDefaultParameters(), $args));
  15506. }
  15507. function toCallback()
  15508. {
  15509. return new Nette\Callback(parent::getDeclaringClass()->getName(), $this->getName());
  15510. }
  15511. function __toString()
  15512. {
  15513. return 'Method ' . parent::getDeclaringClass()->getName() . '::' . $this->getName() . '()';
  15514. }
  15515. function getDeclaringClass()
  15516. {
  15517. return new ClassType(parent::getDeclaringClass()->getName());
  15518. }
  15519. function getPrototype()
  15520. {
  15521. $prototype = parent::getPrototype();
  15522. return new Method($prototype->getDeclaringClass()->getName(), $prototype->getName());
  15523. }
  15524. function getExtension()
  15525. {
  15526. return ($name = $this->getExtensionName()) ? new Extension($name) : NULL;
  15527. }
  15528. function getParameters()
  15529. {
  15530. $me = array(parent::getDeclaringClass()->getName(), $this->getName());
  15531. foreach ($res = parent::getParameters() as $key => $val) {
  15532. $res[$key] = new Parameter($me, $val->getName());
  15533. }
  15534. return $res;
  15535. }
  15536. function hasAnnotation($name)
  15537. {
  15538. $res = AnnotationsParser::getAll($this);
  15539. return !empty($res[$name]);
  15540. }
  15541. function getAnnotation($name)
  15542. {
  15543. $res = AnnotationsParser::getAll($this);
  15544. return isset($res[$name]) ? end($res[$name]) : NULL;
  15545. }
  15546. function getAnnotations()
  15547. {
  15548. return AnnotationsParser::getAll($this);
  15549. }
  15550. function getDescription()
  15551. {
  15552. return $this->getAnnotation('description');
  15553. }
  15554. static function getReflection()
  15555. {
  15556. return new ClassType(get_called_class());
  15557. }
  15558. function __call($name, $args)
  15559. {
  15560. return ObjectMixin::call($this, $name, $args);
  15561. }
  15562. function &__get($name)
  15563. {
  15564. return ObjectMixin::get($this, $name);
  15565. }
  15566. function __set($name, $value)
  15567. {
  15568. return ObjectMixin::set($this, $name, $value);
  15569. }
  15570. function __isset($name)
  15571. {
  15572. return ObjectMixin::has($this, $name);
  15573. }
  15574. function __unset($name)
  15575. {
  15576. ObjectMixin::remove($this, $name);
  15577. }
  15578. static function buildDefaultParameters($params)
  15579. {
  15580. $res = array();
  15581. foreach ($params as $param) {
  15582. $res[$param->getName()] = $param->isDefaultValueAvailable()
  15583. ? $param->getDefaultValue()
  15584. : NULL;
  15585. if ($param->isArray()) {
  15586. settype($res[$param->getName()], 'array');
  15587. }
  15588. }
  15589. return $res;
  15590. }
  15591. static function combineArgs($params, $args)
  15592. {
  15593. $res = array();
  15594. $i = 0;
  15595. foreach ($params as $name => $def) {
  15596. if (isset($args[$name])) {
  15597. $val = $args[$name];
  15598. if ($def !== NULL) {
  15599. settype($val, gettype($def));
  15600. }
  15601. $res[$i++] = $val;
  15602. } else {
  15603. $res[$i++] = $def;
  15604. }
  15605. }
  15606. return $res;
  15607. }
  15608. }
  15609. class Parameter extends \ReflectionParameter
  15610. {
  15611. private $function;
  15612. function __construct($function, $parameter)
  15613. {
  15614. parent::__construct($this->function = $function, $parameter);
  15615. }
  15616. function getClass()
  15617. {
  15618. return ($ref = parent::getClass()) ? new ClassType($ref->getName()) : NULL;
  15619. }
  15620. function getClassName()
  15621. {
  15622. return ($tmp = Nette\Utils\Strings::match($this, '#>\s+([a-z0-9_\\\\]+)#i')) ? $tmp[1] : NULL;
  15623. }
  15624. function getDeclaringClass()
  15625. {
  15626. return ($ref = parent::getDeclaringClass()) ? new ClassType($ref->getName()) : NULL;
  15627. }
  15628. function getDeclaringFunction()
  15629. {
  15630. return is_array($this->function)
  15631. ? new Method($this->function[0], $this->function[1])
  15632. : new GlobalFunction($this->function);
  15633. }
  15634. static function getReflection()
  15635. {
  15636. return new ClassType(get_called_class());
  15637. }
  15638. function __call($name, $args)
  15639. {
  15640. return ObjectMixin::call($this, $name, $args);
  15641. }
  15642. function &__get($name)
  15643. {
  15644. return ObjectMixin::get($this, $name);
  15645. }
  15646. function __set($name, $value)
  15647. {
  15648. return ObjectMixin::set($this, $name, $value);
  15649. }
  15650. function __isset($name)
  15651. {
  15652. return ObjectMixin::has($this, $name);
  15653. }
  15654. function __unset($name)
  15655. {
  15656. ObjectMixin::remove($this, $name);
  15657. }
  15658. }
  15659. class Property extends \ReflectionProperty
  15660. {
  15661. function __toString()
  15662. {
  15663. return 'Property ' . parent::getDeclaringClass()->getName() . '::$' . $this->getName();
  15664. }
  15665. function getDeclaringClass()
  15666. {
  15667. return new ClassType(parent::getDeclaringClass()->getName());
  15668. }
  15669. function hasAnnotation($name)
  15670. {
  15671. $res = AnnotationsParser::getAll($this);
  15672. return !empty($res[$name]);
  15673. }
  15674. function getAnnotation($name)
  15675. {
  15676. $res = AnnotationsParser::getAll($this);
  15677. return isset($res[$name]) ? end($res[$name]) : NULL;
  15678. }
  15679. function getAnnotations()
  15680. {
  15681. return AnnotationsParser::getAll($this);
  15682. }
  15683. function getDescription()
  15684. {
  15685. return $this->getAnnotation('description');
  15686. }
  15687. static function getReflection()
  15688. {
  15689. return new ClassType(get_called_class());
  15690. }
  15691. function __call($name, $args)
  15692. {
  15693. return ObjectMixin::call($this, $name, $args);
  15694. }
  15695. function &__get($name)
  15696. {
  15697. return ObjectMixin::get($this, $name);
  15698. }
  15699. function __set($name, $value)
  15700. {
  15701. return ObjectMixin::set($this, $name, $value);
  15702. }
  15703. function __isset($name)
  15704. {
  15705. return ObjectMixin::has($this, $name);
  15706. }
  15707. function __unset($name)
  15708. {
  15709. ObjectMixin::remove($this, $name);
  15710. }
  15711. }
  15712. }
  15713. namespace Nette\Security {
  15714. use Nette;
  15715. class AuthenticationException extends \Exception
  15716. {
  15717. }
  15718. class Identity extends Nette\FreezableObject implements IIdentity
  15719. {
  15720. private $id;
  15721. private $roles;
  15722. private $data;
  15723. function __construct($id, $roles = NULL, $data = NULL)
  15724. {
  15725. $this->setId($id);
  15726. $this->setRoles((array) $roles);
  15727. $this->data = $data instanceof \Traversable ? iterator_to_array($data) : (array) $data;
  15728. }
  15729. function setId($id)
  15730. {
  15731. $this->updating();
  15732. $this->id = is_numeric($id) ? 1 * $id : $id;
  15733. return $this;
  15734. }
  15735. function getId()
  15736. {
  15737. return $this->id;
  15738. }
  15739. function setRoles(array $roles)
  15740. {
  15741. $this->updating();
  15742. $this->roles = $roles;
  15743. return $this;
  15744. }
  15745. function getRoles()
  15746. {
  15747. return $this->roles;
  15748. }
  15749. function getData()
  15750. {
  15751. return $this->data;
  15752. }
  15753. function __set($key, $value)
  15754. {
  15755. $this->updating();
  15756. if (parent::__isset($key)) {
  15757. parent::__set($key, $value);
  15758. } else {
  15759. $this->data[$key] = $value;
  15760. }
  15761. }
  15762. function &__get($key)
  15763. {
  15764. if (parent::__isset($key)) {
  15765. return parent::__get($key);
  15766. } else {
  15767. return $this->data[$key];
  15768. }
  15769. }
  15770. function __isset($key)
  15771. {
  15772. return isset($this->data[$key]) || parent::__isset($key);
  15773. }
  15774. function __unset($name)
  15775. {
  15776. ObjectMixin::remove($this, $name);
  15777. }
  15778. }
  15779. class Permission extends Nette\Object implements IAuthorizator
  15780. {
  15781. private $roles = array();
  15782. private $resources = array();
  15783. private $rules = array(
  15784. 'allResources' => array(
  15785. 'allRoles' => array(
  15786. 'allPrivileges' => array(
  15787. 'type' => self::DENY,
  15788. 'assert' => NULL,
  15789. ),
  15790. 'byPrivilege' => array(),
  15791. ),
  15792. 'byRole' => array(),
  15793. ),
  15794. 'byResource' => array(),
  15795. );
  15796. private $queriedRole, $queriedResource;
  15797. function addRole($role, $parents = NULL)
  15798. {
  15799. $this->checkRole($role, FALSE);
  15800. if (isset($this->roles[$role])) {
  15801. throw new Nette\InvalidStateException("Role '$role' already exists in the list.");
  15802. }
  15803. $roleParents = array();
  15804. if ($parents !== NULL) {
  15805. if (!is_array($parents)) {
  15806. $parents = array($parents);
  15807. }
  15808. foreach ($parents as $parent) {
  15809. $this->checkRole($parent);
  15810. $roleParents[$parent] = TRUE;
  15811. $this->roles[$parent]['children'][$role] = TRUE;
  15812. }
  15813. }
  15814. $this->roles[$role] = array(
  15815. 'parents' => $roleParents,
  15816. 'children' => array(),
  15817. );
  15818. return $this;
  15819. }
  15820. function hasRole($role)
  15821. {
  15822. $this->checkRole($role, FALSE);
  15823. return isset($this->roles[$role]);
  15824. }
  15825. private function checkRole($role, $need = TRUE)
  15826. {
  15827. if (!is_string($role) || $role === '') {
  15828. throw new Nette\InvalidArgumentException("Role must be a non-empty string.");
  15829. } elseif ($need && !isset($this->roles[$role])) {
  15830. throw new Nette\InvalidStateException("Role '$role' does not exist.");
  15831. }
  15832. }
  15833. function getRoleParents($role)
  15834. {
  15835. $this->checkRole($role);
  15836. return array_keys($this->roles[$role]['parents']);
  15837. }
  15838. function roleInheritsFrom($role, $inherit, $onlyParents = FALSE)
  15839. {
  15840. $this->checkRole($role);
  15841. $this->checkRole($inherit);
  15842. $inherits = isset($this->roles[$role]['parents'][$inherit]);
  15843. if ($inherits || $onlyParents) {
  15844. return $inherits;
  15845. }
  15846. foreach ($this->roles[$role]['parents'] as $parent => $foo) {
  15847. if ($this->roleInheritsFrom($parent, $inherit)) {
  15848. return TRUE;
  15849. }
  15850. }
  15851. return FALSE;
  15852. }
  15853. function removeRole($role)
  15854. {
  15855. $this->checkRole($role);
  15856. foreach ($this->roles[$role]['children'] as $child => $foo) {
  15857. unset($this->roles[$child]['parents'][$role]);
  15858. }
  15859. foreach ($this->roles[$role]['parents'] as $parent => $foo) {
  15860. unset($this->roles[$parent]['children'][$role]);
  15861. }
  15862. unset($this->roles[$role]);
  15863. foreach ($this->rules['allResources']['byRole'] as $roleCurrent => $rules) {
  15864. if ($role === $roleCurrent) {
  15865. unset($this->rules['allResources']['byRole'][$roleCurrent]);
  15866. }
  15867. }
  15868. foreach ($this->rules['byResource'] as $resourceCurrent => $visitor) {
  15869. if (isset($visitor['byRole'])) {
  15870. foreach ($visitor['byRole'] as $roleCurrent => $rules) {
  15871. if ($role === $roleCurrent) {
  15872. unset($this->rules['byResource'][$resourceCurrent]['byRole'][$roleCurrent]);
  15873. }
  15874. }
  15875. }
  15876. }
  15877. return $this;
  15878. }
  15879. function removeAllRoles()
  15880. {
  15881. $this->roles = array();
  15882. foreach ($this->rules['allResources']['byRole'] as $roleCurrent => $rules) {
  15883. unset($this->rules['allResources']['byRole'][$roleCurrent]);
  15884. }
  15885. foreach ($this->rules['byResource'] as $resourceCurrent => $visitor) {
  15886. foreach ($visitor['byRole'] as $roleCurrent => $rules) {
  15887. unset($this->rules['byResource'][$resourceCurrent]['byRole'][$roleCurrent]);
  15888. }
  15889. }
  15890. return $this;
  15891. }
  15892. function addResource($resource, $parent = NULL)
  15893. {
  15894. $this->checkResource($resource, FALSE);
  15895. if (isset($this->resources[$resource])) {
  15896. throw new Nette\InvalidStateException("Resource '$resource' already exists in the list.");
  15897. }
  15898. if ($parent !== NULL) {
  15899. $this->checkResource($parent);
  15900. $this->resources[$parent]['children'][$resource] = TRUE;
  15901. }
  15902. $this->resources[$resource] = array(
  15903. 'parent' => $parent,
  15904. 'children' => array()
  15905. );
  15906. return $this;
  15907. }
  15908. function hasResource($resource)
  15909. {
  15910. $this->checkResource($resource, FALSE);
  15911. return isset($this->resources[$resource]);
  15912. }
  15913. private function checkResource($resource, $need = TRUE)
  15914. {
  15915. if (!is_string($resource) || $resource === '') {
  15916. throw new Nette\InvalidArgumentException("Resource must be a non-empty string.");
  15917. } elseif ($need && !isset($this->resources[$resource])) {
  15918. throw new Nette\InvalidStateException("Resource '$resource' does not exist.");
  15919. }
  15920. }
  15921. function resourceInheritsFrom($resource, $inherit, $onlyParent = FALSE)
  15922. {
  15923. $this->checkResource($resource);
  15924. $this->checkResource($inherit);
  15925. if ($this->resources[$resource]['parent'] === NULL) {
  15926. return FALSE;
  15927. }
  15928. $parent = $this->resources[$resource]['parent'];
  15929. if ($inherit === $parent) {
  15930. return TRUE;
  15931. } elseif ($onlyParent) {
  15932. return FALSE;
  15933. }
  15934. while ($this->resources[$parent]['parent'] !== NULL) {
  15935. $parent = $this->resources[$parent]['parent'];
  15936. if ($inherit === $parent) {
  15937. return TRUE;
  15938. }
  15939. }
  15940. return FALSE;
  15941. }
  15942. function removeResource($resource)
  15943. {
  15944. $this->checkResource($resource);
  15945. $parent = $this->resources[$resource]['parent'];
  15946. if ($parent !== NULL) {
  15947. unset($this->resources[$parent]['children'][$resource]);
  15948. }
  15949. $removed = array($resource);
  15950. foreach ($this->resources[$resource]['children'] as $child => $foo) {
  15951. $this->removeResource($child);
  15952. $removed[] = $child;
  15953. }
  15954. foreach ($removed as $resourceRemoved) {
  15955. foreach ($this->rules['byResource'] as $resourceCurrent => $rules) {
  15956. if ($resourceRemoved === $resourceCurrent) {
  15957. unset($this->rules['byResource'][$resourceCurrent]);
  15958. }
  15959. }
  15960. }
  15961. unset($this->resources[$resource]);
  15962. return $this;
  15963. }
  15964. function removeAllResources()
  15965. {
  15966. foreach ($this->resources as $resource => $foo) {
  15967. foreach ($this->rules['byResource'] as $resourceCurrent => $rules) {
  15968. if ($resource === $resourceCurrent) {
  15969. unset($this->rules['byResource'][$resourceCurrent]);
  15970. }
  15971. }
  15972. }
  15973. $this->resources = array();
  15974. return $this;
  15975. }
  15976. function allow($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL, $assertion = NULL)
  15977. {
  15978. $this->setRule(TRUE, self::ALLOW, $roles, $resources, $privileges, $assertion);
  15979. return $this;
  15980. }
  15981. function deny($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL, $assertion = NULL)
  15982. {
  15983. $this->setRule(TRUE, self::DENY, $roles, $resources, $privileges, $assertion);
  15984. return $this;
  15985. }
  15986. function removeAllow($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL)
  15987. {
  15988. $this->setRule(FALSE, self::ALLOW, $roles, $resources, $privileges);
  15989. return $this;
  15990. }
  15991. function removeDeny($roles = self::ALL, $resources = self::ALL, $pri