no message
This commit is contained in:
@@ -0,0 +1 @@
|
||||
app/config/local.neon
|
||||
@@ -0,0 +1,9 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule ^$ www/ [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !^www/
|
||||
RewriteRule ^(.*)$ www/$1
|
||||
</IfModule>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$configurator = new Nette\Configurator;
|
||||
|
||||
//$configurator->setDebugMode('23.75.345.200'); // enable for your remote IP
|
||||
$configurator->enableTracy(__DIR__ . '/../log');
|
||||
|
||||
$configurator->setTimeZone('Europe/Prague');
|
||||
$configurator->setTempDirectory(__DIR__ . '/../temp');
|
||||
|
||||
$configurator->createRobotLoader()
|
||||
->addDirectory(__DIR__)
|
||||
->register();
|
||||
|
||||
$configurator->addConfig(__DIR__ . '/config/config.neon');
|
||||
$configurator->addConfig(__DIR__ . '/config/config.local.neon');
|
||||
|
||||
$container = $configurator->createContainer();
|
||||
|
||||
return $container;
|
||||
@@ -0,0 +1,7 @@
|
||||
parameters:
|
||||
|
||||
|
||||
database:
|
||||
dsn: 'mysql:host=127.0.0.1;dbname=test'
|
||||
user:
|
||||
password:
|
||||
@@ -0,0 +1,16 @@
|
||||
parameters:
|
||||
|
||||
|
||||
application:
|
||||
errorPresenter: Error
|
||||
mapping:
|
||||
*: App\*Module\Presenters\*Presenter
|
||||
|
||||
|
||||
session:
|
||||
expiration: 14 days
|
||||
|
||||
|
||||
services:
|
||||
router: App\RouterFactory::createRouter
|
||||
- App\Model\CalculatorManager
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/*
|
||||
MODEL
|
||||
// kalkulačka
|
||||
// 4 základní operace
|
||||
//tj. sčítání, odčítání, násobení a dělení
|
||||
|
||||
//trida CalculatorManager (class)
|
||||
//1. pouziva php trait Nette/SmartObject ()
|
||||
//TRAITS: mechanism for code reuse in single inheritance
|
||||
//intended to reduce some limitations of single inheritance
|
||||
//by enabling a developer to reuse sets of methods freely in
|
||||
//several independent classes living in different class hierarchies
|
||||
//*similar to a class,but only intended to group functionality in a fine-grained and consistent way
|
||||
//e.g:
|
||||
/*
|
||||
trait ezcReflectionReturnInfo {
|
||||
function getReturnType() { }
|
||||
function getReturnDescription() { }
|
||||
}
|
||||
class ezcReflectionMethod extends ReflectionMethod {
|
||||
use ezcReflectionReturnInfo;
|
||||
}
|
||||
//2. definuje 4 operace: sčítání, odčítání, násobení a dělení
|
||||
|
||||
*/
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use Nette\SmartObject;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Model operací kalkulačky.
|
||||
* @package App\Model
|
||||
*/
|
||||
class CalculatorManager{
|
||||
|
||||
use SmartObject;
|
||||
|
||||
/** Definice konstant pro operace. */
|
||||
const
|
||||
ADD = 1,
|
||||
SUBTRACT = 2,
|
||||
MULTIPLY = 3,
|
||||
DIVIDE = 4;
|
||||
|
||||
/**
|
||||
* Getter pro existující operace.
|
||||
* @return array asociativní pole konstant pro operace a jejich slovního pojmenování
|
||||
*/
|
||||
public function getOperations(){
|
||||
return array(
|
||||
self::ADD => 'Sčítání',
|
||||
self::SUBTRACT => 'Odčítání',
|
||||
self::MULTIPLY => 'Násobení',
|
||||
self::DIVIDE => 'Dělení'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zavolá zadanou operaci a vrátí její výsledek.
|
||||
* @param int $operation zadaná operace
|
||||
* @param int $x první číslo pro operaci
|
||||
* @param int $y druhé číslo pro operaci
|
||||
* @return int|null výsledek operace nebo null, pokud zadaná operace neexistuje
|
||||
*/
|
||||
public function calculate($operation, $x, $y){
|
||||
switch ($operation) {
|
||||
case self::ADD:
|
||||
return $this->add($x, $y);
|
||||
case self::SUBTRACT:
|
||||
return $this->subtract($x, $y);
|
||||
case self::MULTIPLY:
|
||||
return $this->multiply($x, $y);
|
||||
case self::DIVIDE:
|
||||
return $this->divide($x, $y);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sečte daná čísla a vrátí výsledek.
|
||||
* @param int $x první číslo
|
||||
* @param int $y druhé číslo
|
||||
* @return int výsledek po sčítání
|
||||
*/
|
||||
public function add($x, $y){
|
||||
return $x + $y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Odečte druhé číslo od prvního a vrátí výsledek.
|
||||
* @param int $x první číslo
|
||||
* @param int $y druhé číslo
|
||||
* @return int výsledek po odčítání
|
||||
*/
|
||||
public function subtract($x, $y)
|
||||
{
|
||||
return $x - $y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vynásobí daná čísla a vrátí výsledek.
|
||||
* @param int $x první číslo
|
||||
* @param int $y druhé číslo
|
||||
* @return int výsledek po násobení
|
||||
*/
|
||||
public function multiply($x, $y)
|
||||
{
|
||||
return $x * $y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vydělí první číslo druhým bezezbytku a vrátí výsledek.
|
||||
* @param int $x první číslo
|
||||
* @param int $y druhé číslo; nesmí být 0
|
||||
* @return int výsledek po dělení bezezbytku
|
||||
*/
|
||||
public function divide($x, $y)
|
||||
{
|
||||
return round($x / $y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Presenters;
|
||||
|
||||
|
||||
use Nette\Application\UI\Presenter;
|
||||
use App\Model\CalculatorManager;
|
||||
use Nette\Application\UI\Form;
|
||||
use Nette\Utils\ArrayHash;
|
||||
|
||||
/**
|
||||
* Presenter kalkulačky.
|
||||
* @package App\Presenters
|
||||
*/
|
||||
|
||||
class CalculatorPresenter extends Presenter{
|
||||
|
||||
const
|
||||
FORM_MSG_REQUIRED = 'Tohle pole je povinné.',
|
||||
FORM_MSG_RULE = 'Tohle pole má neplatný formát.';
|
||||
|
||||
/** @var CalculatorManager Instance třídy modelu pro práci s operacemi kalkulačky. */
|
||||
private $calculatorManager;
|
||||
|
||||
/** @var int|null výsledek operace nebo null */
|
||||
private $result = null;
|
||||
|
||||
public function __construct(CalculatorManager $calculatorManager){
|
||||
parent::__construct();
|
||||
$this->calculatorManager = $calculatorManager;
|
||||
}
|
||||
|
||||
/** Výchozí vykreslovací metoda tohoto presenteru. */
|
||||
public function renderDefault(){
|
||||
// Předání výsledku do šablony.
|
||||
$this->template->result = $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí formulář kalkulačky.
|
||||
* @return Form formulář kalkulačky
|
||||
*/
|
||||
|
||||
protected function createComponentCalculatorForm(){
|
||||
|
||||
$form = new Form;
|
||||
// Získáme existující operace kalkulačky a dáme je do výběru operací.
|
||||
$form->addRadioList('operation', 'Operace:', $this->calculatorManager->getOperations())
|
||||
->setDefaultValue(CalculatorManager::ADD)
|
||||
->setRequired(self::FORM_MSG_REQUIRED);
|
||||
$form->addText('x', 'První číslo:')
|
||||
->setType('number')
|
||||
->setDefaultValue(0)
|
||||
->setRequired(self::FORM_MSG_REQUIRED)
|
||||
->addRule(Form::INTEGER, self::FORM_MSG_RULE);
|
||||
$form->addText('y', 'Druhé číslo:')
|
||||
->setType('number')
|
||||
->setDefaultValue(0)
|
||||
->setRequired(self::FORM_MSG_REQUIRED)
|
||||
->addRule(Form::INTEGER, self::FORM_MSG_RULE)
|
||||
// Ošetříme dělení nulou.
|
||||
->addConditionOn($form['operation'], Form::EQUAL, CalculatorManager::DIVIDE)
|
||||
->addRule(Form::PATTERN, 'Nelze dělit nulou.', '^[^0].*');
|
||||
$form->addSubmit('calculate', 'Spočítej výsledek');
|
||||
$form->onSuccess[] = [$this, 'calculatorFormSucceeded'];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Funkce se vykonaná při úspěšném odeslání formuláře kalkulačky a zpracuje odeslané hodnoty.
|
||||
* @param Form $form formulář kalkulačky
|
||||
* @param ArrayHash $values odeslané hodnoty formuláře
|
||||
*/
|
||||
public function calculatorFormSucceeded($form, $values) {
|
||||
// Necháme si vypočítat výsledek podle zvolené operace a zadaných hodnot.
|
||||
$this->result = $this->calculatorManager->calculate($values->operation, $values->x, $values->y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Presenters;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
final class Error4xxPresenter extends Nette\Application\UI\Presenter
|
||||
{
|
||||
public function startup(): void
|
||||
{
|
||||
parent::startup();
|
||||
if (!$this->getRequest()->isMethod(Nette\Application\Request::FORWARD)) {
|
||||
$this->error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function renderDefault(Nette\Application\BadRequestException $exception): void
|
||||
{
|
||||
// load template 403.latte or 404.latte or ... 4xx.latte
|
||||
$file = __DIR__ . "/templates/Error/{$exception->getCode()}.latte";
|
||||
$this->template->setFile(is_file($file) ? $file : __DIR__ . '/templates/Error/4xx.latte');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Presenters;
|
||||
|
||||
use Nette;
|
||||
use Nette\Application\Responses;
|
||||
use Nette\Http;
|
||||
use Tracy\ILogger;
|
||||
|
||||
|
||||
final class ErrorPresenter implements Nette\Application\IPresenter
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
|
||||
public function __construct(ILogger $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
||||
public function run(Nette\Application\Request $request): Nette\Application\IResponse
|
||||
{
|
||||
$exception = $request->getParameter('exception');
|
||||
|
||||
if ($exception instanceof Nette\Application\BadRequestException) {
|
||||
[$module, , $sep] = Nette\Application\Helpers::splitName($request->getPresenterName());
|
||||
return new Responses\ForwardResponse($request->setPresenterName($module . $sep . 'Error4xx'));
|
||||
}
|
||||
|
||||
$this->logger->log($exception, ILogger::EXCEPTION);
|
||||
return new Responses\CallbackResponse(function (Http\IRequest $httpRequest, Http\IResponse $httpResponse): void {
|
||||
if (preg_match('#^text/html(?:;|$)#', (string) $httpResponse->getHeader('Content-Type'))) {
|
||||
require __DIR__ . '/templates/Error/500.phtml';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<title>{ifset title}{include title|stripHtml} | {/ifset}Nette Web</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">{$flash->message}</div>
|
||||
|
||||
{include content}
|
||||
|
||||
{block scripts}
|
||||
<script src="https://nette.github.io/resources/js/3/netteForms.min.js"></script>
|
||||
{/block}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
{block content}
|
||||
<div id="banner">
|
||||
<h1 n:block=title>Calculator!</h1>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h3 n:ifset=$result>Result is: {$result}</h3>
|
||||
{control calculatorForm}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
html { font: normal 18px/1.3 Georgia, "New York CE", utopia, serif; color: #666; -webkit-text-stroke: 1px rgba(0,0,0,0); overflow-y: scroll; }
|
||||
body { background: #3484d2; color: #333; margin: 2em auto; padding: 0 .5em; max-width: 600px; min-width: 320px; }
|
||||
|
||||
a { color: #006aeb; padding: 3px 1px; }
|
||||
a:hover, a:active, a:focus { background-color: #006aeb; text-decoration: none; color: white; }
|
||||
|
||||
#banner { border-radius: 12px 12px 0 0; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAB5CAMAAADPursXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGBQTFRFD1CRDkqFDTlmDkF1D06NDT1tDTNZDk2KEFWaDTZgDkiCDTtpDT5wDkZ/DTBVEFacEFOWD1KUDTRcDTFWDkV9DkR7DkN4DkByDTVeDC9TDThjDTxrDkeADkuIDTRbDC9SbsUaggAAAEdJREFUeNqkwYURgAAQA7DH3d3335LSKyxAYpf9vWCpnYbf01qcOdFVXc14w4BznNTjkQfsscAdU3b4wIh9fDVYc4zV8xZgAAYaCMI6vPgLAAAAAElFTkSuQmCC); }
|
||||
|
||||
h1 { font: inherit; color: white; font-size: 50px; line-height: 121px; margin: 0; padding-left: 4%; background: url(https://files.nette.org/images/logo-nette@2.png) no-repeat 95%; background-size: 130px auto; text-shadow: 1px 1px 0 rgba(0, 0, 0, .9); }
|
||||
@media (max-width: 600px) {
|
||||
h1 { background: none; font-size: 40px; }
|
||||
}
|
||||
|
||||
#content { background: white; border: 1px solid #eff4f7; border-radius: 0 0 12px 12px; padding: 10px 4%; overflow: hidden; }
|
||||
|
||||
h2 { font: inherit; padding: 1.2em 0; margin: 0; }
|
||||
|
||||
img { border: none; float: right; margin: 0 0 1em 3em; }
|
||||
</style>
|
||||
{/block}
|
||||
@@ -0,0 +1,7 @@
|
||||
{block content}
|
||||
<h1 n:block=title>Access Denied</h1>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p><small>error 403</small></p>
|
||||
@@ -0,0 +1,8 @@
|
||||
{block content}
|
||||
<h1 n:block=title>Page Not Found</h1>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p><small>error 404</small></p>
|
||||
@@ -0,0 +1,6 @@
|
||||
{block content}
|
||||
<h1 n:block=title>Method Not Allowed</h1>
|
||||
|
||||
<p>The requested method is not allowed for the URL.</p>
|
||||
|
||||
<p><small>error 405</small></p>
|
||||
@@ -0,0 +1,6 @@
|
||||
{block content}
|
||||
<h1 n:block=title>Page Not Found</h1>
|
||||
|
||||
<p>The page you requested has been taken off the site. We apologize for the inconvenience.</p>
|
||||
|
||||
<p><small>error 410</small></p>
|
||||
@@ -0,0 +1,4 @@
|
||||
{block content}
|
||||
<h1 n:block=title>Oops...</h1>
|
||||
|
||||
<p>Your browser sent a request that this server could not understand or process.</p>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html><!-- "' --></textarea></script></style></pre></xmp></a></audio></button></canvas></datalist></details></dialog></iframe></listing></meter></noembed></noframes></noscript></optgroup></option></progress></rp></select></table></template></title></video>
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>Server Error</title>
|
||||
|
||||
<style>
|
||||
#nette-error { all: initial; position: absolute; top: 0; left: 0; right: 0; height: 70vh; min-height: 400px; display: flex; align-items: center; justify-content: center; z-index: 1000 }
|
||||
#nette-error div { all: initial; max-width: 550px; background: white; color: #333; display: block }
|
||||
#nette-error h1 { all: initial; font: bold 50px/1.1 sans-serif; display: block; margin: 40px }
|
||||
#nette-error p { all: initial; font: 20px/1.4 sans-serif; margin: 40px; display: block }
|
||||
#nette-error small { color: gray }
|
||||
</style>
|
||||
|
||||
<div id=nette-error>
|
||||
<div>
|
||||
<h1>Server Error</h1>
|
||||
|
||||
<p>We're sorry! The server encountered an internal error and
|
||||
was unable to complete your request. Please try again later.</p>
|
||||
|
||||
<p><small>error 500</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.body.insertBefore(document.getElementById('nette-error'), document.body.firstChild);
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
header('HTTP/1.1 503 Service Unavailable');
|
||||
header('Retry-After: 300'); // 5 minutes in seconds
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="noindex">
|
||||
<meta name="generator" content="Nette Framework">
|
||||
|
||||
<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 }
|
||||
</style>
|
||||
|
||||
<title>Site is temporarily down for maintenance</title>
|
||||
|
||||
<h1>We're Sorry</h1>
|
||||
|
||||
<p>The site is temporarily down for maintenance. Please try again in a few minutes.</p>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
|
||||
use Nette\Application\Routers\IRoute;
|
||||
use Nette\Application\Routers\Route;
|
||||
use Nette\Application\Routers\RouteList;
|
||||
use Nette\StaticClass;
|
||||
|
||||
class RouterFactory
|
||||
{
|
||||
use StaticClass;
|
||||
|
||||
/**
|
||||
* @return IRouter
|
||||
*/
|
||||
|
||||
public static function createRouter()
|
||||
{
|
||||
$router = new RouteList;
|
||||
$router[]= new Route('<presenter>/<action>[/<id>]', 'Calculator:default');
|
||||
return $router;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "nette/web-project",
|
||||
"description": "Nette: Standard Web Project",
|
||||
"keywords": ["nette"],
|
||||
"type": "project",
|
||||
"license": ["MIT", "BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"require": {
|
||||
"php": ">= 7.1",
|
||||
"nette/application": "^3.0",
|
||||
"nette/bootstrap": "^3.0",
|
||||
"nette/caching": "^3.0",
|
||||
"nette/database": "^3.0",
|
||||
"nette/di": "^3.0",
|
||||
"nette/finder": "^2.5",
|
||||
"nette/forms": "^3.0",
|
||||
"nette/http": "^3.0",
|
||||
"nette/mail": "^3.0",
|
||||
"nette/robot-loader": "^3.0",
|
||||
"nette/security": "^3.0",
|
||||
"nette/utils": "^3.0",
|
||||
"latte/latte": "^2.5",
|
||||
"tracy/tracy": "^2.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["app/Booting.php"]
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1366
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,61 @@
|
||||
Nette Web Project
|
||||
=================
|
||||
|
||||
This is a simple, skeleton application using the [Nette](https://nette.org). This is meant to
|
||||
be used as a starting point for your new projects.
|
||||
|
||||
[Nette](https://nette.org) is a popular tool for PHP web development.
|
||||
It is designed to be the most usable and friendliest as possible. It focuses
|
||||
on security and performance and is definitely one of the safest PHP frameworks.
|
||||
|
||||
If you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you!
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- Web Project for Nette 3.0 requires PHP 7.1
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The best way to install Web Project is using Composer. If you don't have Composer yet,
|
||||
download it following [the instructions](https://doc.nette.org/composer). Then use command:
|
||||
|
||||
composer create-project nette/web-project path/to/install
|
||||
cd path/to/install
|
||||
|
||||
|
||||
Make directories `temp/` and `log/` writable.
|
||||
|
||||
|
||||
Web Server Setup
|
||||
----------------
|
||||
|
||||
The simplest way to get started is to start the built-in PHP server in the root directory of your project:
|
||||
|
||||
php -S localhost:8000 -t www
|
||||
|
||||
Then visit `http://localhost:8000` in your browser to see the welcome page.
|
||||
|
||||
For Apache or Nginx, setup a virtual host to point to the `www/` directory of the project and you
|
||||
should be ready to go.
|
||||
|
||||
**It is CRITICAL that whole `app/`, `log/` and `temp/` directories are not accessible directly
|
||||
via a web browser. See [security warning](https://nette.org/security-warning).**
|
||||
|
||||
|
||||
Notice: Composer PHP version
|
||||
----------------------------
|
||||
|
||||
This project forces PHP 5.6 (eventually 7.1) as your PHP version for Composer packages. If you have newer
|
||||
version on production server you should change it in `composer.json`:
|
||||
|
||||
```json
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit1e4e7be4b54097efddcd9fd472eda330::getLoader();
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../nette/tester/src" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/tester" "$@"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../nette/tester/src/tester
|
||||
php "%BIN_TARGET%" %*
|
||||
Vendored
+445
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Latte\\CompileException' => $vendorDir . '/latte/latte/src/Latte/exceptions.php',
|
||||
'Latte\\Compiler' => $vendorDir . '/latte/latte/src/Latte/Compiler/Compiler.php',
|
||||
'Latte\\Engine' => $vendorDir . '/latte/latte/src/Latte/Engine.php',
|
||||
'Latte\\Helpers' => $vendorDir . '/latte/latte/src/Latte/Helpers.php',
|
||||
'Latte\\HtmlNode' => $vendorDir . '/latte/latte/src/Latte/Compiler/HtmlNode.php',
|
||||
'Latte\\ILoader' => $vendorDir . '/latte/latte/src/Latte/ILoader.php',
|
||||
'Latte\\IMacro' => $vendorDir . '/latte/latte/src/Latte/IMacro.php',
|
||||
'Latte\\Loaders\\FileLoader' => $vendorDir . '/latte/latte/src/Latte/Loaders/FileLoader.php',
|
||||
'Latte\\Loaders\\StringLoader' => $vendorDir . '/latte/latte/src/Latte/Loaders/StringLoader.php',
|
||||
'Latte\\MacroNode' => $vendorDir . '/latte/latte/src/Latte/Compiler/MacroNode.php',
|
||||
'Latte\\MacroTokens' => $vendorDir . '/latte/latte/src/Latte/Compiler/MacroTokens.php',
|
||||
'Latte\\Macros\\BlockMacros' => $vendorDir . '/latte/latte/src/Latte/Macros/BlockMacros.php',
|
||||
'Latte\\Macros\\CoreMacros' => $vendorDir . '/latte/latte/src/Latte/Macros/CoreMacros.php',
|
||||
'Latte\\Macros\\MacroSet' => $vendorDir . '/latte/latte/src/Latte/Macros/MacroSet.php',
|
||||
'Latte\\Parser' => $vendorDir . '/latte/latte/src/Latte/Compiler/Parser.php',
|
||||
'Latte\\PhpHelpers' => $vendorDir . '/latte/latte/src/Latte/Compiler/PhpHelpers.php',
|
||||
'Latte\\PhpWriter' => $vendorDir . '/latte/latte/src/Latte/Compiler/PhpWriter.php',
|
||||
'Latte\\RegexpException' => $vendorDir . '/latte/latte/src/Latte/exceptions.php',
|
||||
'Latte\\RuntimeException' => $vendorDir . '/latte/latte/src/Latte/exceptions.php',
|
||||
'Latte\\Runtime\\CachingIterator' => $vendorDir . '/latte/latte/src/Latte/Runtime/CachingIterator.php',
|
||||
'Latte\\Runtime\\FilterExecutor' => $vendorDir . '/latte/latte/src/Latte/Runtime/FilterExecutor.php',
|
||||
'Latte\\Runtime\\FilterInfo' => $vendorDir . '/latte/latte/src/Latte/Runtime/FilterInfo.php',
|
||||
'Latte\\Runtime\\Filters' => $vendorDir . '/latte/latte/src/Latte/Runtime/Filters.php',
|
||||
'Latte\\Runtime\\Html' => $vendorDir . '/latte/latte/src/Latte/Runtime/Html.php',
|
||||
'Latte\\Runtime\\IHtmlString' => $vendorDir . '/latte/latte/src/Latte/Runtime/IHtmlString.php',
|
||||
'Latte\\Runtime\\ISnippetBridge' => $vendorDir . '/latte/latte/src/Latte/Runtime/ISnippetBridge.php',
|
||||
'Latte\\Runtime\\SnippetDriver' => $vendorDir . '/latte/latte/src/Latte/Runtime/SnippetDriver.php',
|
||||
'Latte\\Runtime\\Template' => $vendorDir . '/latte/latte/src/Latte/Runtime/Template.php',
|
||||
'Latte\\Strict' => $vendorDir . '/latte/latte/src/Latte/Strict.php',
|
||||
'Latte\\Token' => $vendorDir . '/latte/latte/src/Latte/Compiler/Token.php',
|
||||
'Latte\\TokenIterator' => $vendorDir . '/latte/latte/src/Latte/Compiler/TokenIterator.php',
|
||||
'Latte\\Tokenizer' => $vendorDir . '/latte/latte/src/Latte/Compiler/Tokenizer.php',
|
||||
'NetteModule\\ErrorPresenter' => $vendorDir . '/nette/application/src/Application/ErrorPresenter.php',
|
||||
'NetteModule\\MicroPresenter' => $vendorDir . '/nette/application/src/Application/MicroPresenter.php',
|
||||
'Nette\\Application\\AbortException' => $vendorDir . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\Application' => $vendorDir . '/nette/application/src/Application/Application.php',
|
||||
'Nette\\Application\\ApplicationException' => $vendorDir . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\BadRequestException' => $vendorDir . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\ForbiddenRequestException' => $vendorDir . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\Helpers' => $vendorDir . '/nette/application/src/Application/Helpers.php',
|
||||
'Nette\\Application\\IPresenter' => $vendorDir . '/nette/application/src/Application/IPresenter.php',
|
||||
'Nette\\Application\\IPresenterFactory' => $vendorDir . '/nette/application/src/Application/IPresenterFactory.php',
|
||||
'Nette\\Application\\IResponse' => $vendorDir . '/nette/application/src/Application/IResponse.php',
|
||||
'Nette\\Application\\IRouter' => $vendorDir . '/nette/application/src/Application/IRouter.php',
|
||||
'Nette\\Application\\InvalidPresenterException' => $vendorDir . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\LinkGenerator' => $vendorDir . '/nette/application/src/Application/LinkGenerator.php',
|
||||
'Nette\\Application\\PresenterFactory' => $vendorDir . '/nette/application/src/Application/PresenterFactory.php',
|
||||
'Nette\\Application\\Request' => $vendorDir . '/nette/application/src/Application/Request.php',
|
||||
'Nette\\Application\\Responses\\CallbackResponse' => $vendorDir . '/nette/application/src/Application/Responses/CallbackResponse.php',
|
||||
'Nette\\Application\\Responses\\FileResponse' => $vendorDir . '/nette/application/src/Application/Responses/FileResponse.php',
|
||||
'Nette\\Application\\Responses\\ForwardResponse' => $vendorDir . '/nette/application/src/Application/Responses/ForwardResponse.php',
|
||||
'Nette\\Application\\Responses\\JsonResponse' => $vendorDir . '/nette/application/src/Application/Responses/JsonResponse.php',
|
||||
'Nette\\Application\\Responses\\RedirectResponse' => $vendorDir . '/nette/application/src/Application/Responses/RedirectResponse.php',
|
||||
'Nette\\Application\\Responses\\TextResponse' => $vendorDir . '/nette/application/src/Application/Responses/TextResponse.php',
|
||||
'Nette\\Application\\Routers\\CliRouter' => $vendorDir . '/nette/application/src/Application/Routers/CliRouter.php',
|
||||
'Nette\\Application\\Routers\\Route' => $vendorDir . '/nette/application/src/Application/Routers/Route.php',
|
||||
'Nette\\Application\\Routers\\RouteList' => $vendorDir . '/nette/application/src/Application/Routers/RouteList.php',
|
||||
'Nette\\Application\\Routers\\SimpleRouter' => $vendorDir . '/nette/application/src/Application/Routers/SimpleRouter.php',
|
||||
'Nette\\Application\\UI\\BadSignalException' => $vendorDir . '/nette/application/src/Application/UI/BadSignalException.php',
|
||||
'Nette\\Application\\UI\\Component' => $vendorDir . '/nette/application/src/Application/UI/Component.php',
|
||||
'Nette\\Application\\UI\\ComponentReflection' => $vendorDir . '/nette/application/src/Application/UI/ComponentReflection.php',
|
||||
'Nette\\Application\\UI\\Control' => $vendorDir . '/nette/application/src/Application/UI/Control.php',
|
||||
'Nette\\Application\\UI\\Form' => $vendorDir . '/nette/application/src/Application/UI/Form.php',
|
||||
'Nette\\Application\\UI\\IRenderable' => $vendorDir . '/nette/application/src/Application/UI/IRenderable.php',
|
||||
'Nette\\Application\\UI\\ISignalReceiver' => $vendorDir . '/nette/application/src/Application/UI/ISignalReceiver.php',
|
||||
'Nette\\Application\\UI\\IStatePersistent' => $vendorDir . '/nette/application/src/Application/UI/IStatePersistent.php',
|
||||
'Nette\\Application\\UI\\ITemplate' => $vendorDir . '/nette/application/src/Application/UI/ITemplate.php',
|
||||
'Nette\\Application\\UI\\ITemplateFactory' => $vendorDir . '/nette/application/src/Application/UI/ITemplateFactory.php',
|
||||
'Nette\\Application\\UI\\InvalidLinkException' => $vendorDir . '/nette/application/src/Application/UI/InvalidLinkException.php',
|
||||
'Nette\\Application\\UI\\Link' => $vendorDir . '/nette/application/src/Application/UI/Link.php',
|
||||
'Nette\\Application\\UI\\MethodReflection' => $vendorDir . '/nette/application/src/Application/UI/MethodReflection.php',
|
||||
'Nette\\Application\\UI\\Multiplier' => $vendorDir . '/nette/application/src/Application/UI/Multiplier.php',
|
||||
'Nette\\Application\\UI\\Presenter' => $vendorDir . '/nette/application/src/Application/UI/Presenter.php',
|
||||
'Nette\\ArgumentOutOfRangeException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\ApplicationExtension' => $vendorDir . '/nette/application/src/Bridges/ApplicationDI/ApplicationExtension.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\LatteExtension' => $vendorDir . '/nette/application/src/Bridges/ApplicationDI/LatteExtension.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\PresenterFactoryCallback' => $vendorDir . '/nette/application/src/Bridges/ApplicationDI/PresenterFactoryCallback.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\RoutingExtension' => $vendorDir . '/nette/application/src/Bridges/ApplicationDI/RoutingExtension.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\ILatteFactory' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/ILatteFactory.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\Loader' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/Loader.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\SnippetBridge' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/SnippetBridge.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\Template' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/Template.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\TemplateFactory' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/TemplateFactory.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\UIMacros' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/UIMacros.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\UIRuntime' => $vendorDir . '/nette/application/src/Bridges/ApplicationLatte/UIRuntime.php',
|
||||
'Nette\\Bridges\\ApplicationTracy\\RoutingPanel' => $vendorDir . '/nette/application/src/Bridges/ApplicationTracy/RoutingPanel.php',
|
||||
'Nette\\Bridges\\CacheDI\\CacheExtension' => $vendorDir . '/nette/caching/src/Bridges/CacheDI/CacheExtension.php',
|
||||
'Nette\\Bridges\\CacheLatte\\CacheMacro' => $vendorDir . '/nette/caching/src/Bridges/CacheLatte/CacheMacro.php',
|
||||
'Nette\\Bridges\\DITracy\\ContainerPanel' => $vendorDir . '/nette/di/src/Bridges/DITracy/ContainerPanel.php',
|
||||
'Nette\\Bridges\\DatabaseDI\\DatabaseExtension' => $vendorDir . '/nette/database/src/Bridges/DatabaseDI/DatabaseExtension.php',
|
||||
'Nette\\Bridges\\DatabaseTracy\\ConnectionPanel' => $vendorDir . '/nette/database/src/Bridges/DatabaseTracy/ConnectionPanel.php',
|
||||
'Nette\\Bridges\\FormsDI\\FormsExtension' => $vendorDir . '/nette/forms/src/Bridges/FormsDI/FormsExtension.php',
|
||||
'Nette\\Bridges\\FormsLatte\\FormMacros' => $vendorDir . '/nette/forms/src/Bridges/FormsLatte/FormMacros.php',
|
||||
'Nette\\Bridges\\FormsLatte\\Runtime' => $vendorDir . '/nette/forms/src/Bridges/FormsLatte/Runtime.php',
|
||||
'Nette\\Bridges\\Framework\\TracyBridge' => $vendorDir . '/nette/bootstrap/src/Bridges/Framework/TracyBridge.php',
|
||||
'Nette\\Bridges\\HttpDI\\HttpExtension' => $vendorDir . '/nette/http/src/Bridges/HttpDI/HttpExtension.php',
|
||||
'Nette\\Bridges\\HttpDI\\SessionExtension' => $vendorDir . '/nette/http/src/Bridges/HttpDI/SessionExtension.php',
|
||||
'Nette\\Bridges\\HttpTracy\\SessionPanel' => $vendorDir . '/nette/http/src/Bridges/HttpTracy/SessionPanel.php',
|
||||
'Nette\\Bridges\\MailDI\\MailExtension' => $vendorDir . '/nette/mail/src/Bridges/MailDI/MailExtension.php',
|
||||
'Nette\\Bridges\\ReflectionDI\\ReflectionExtension' => $vendorDir . '/nette/reflection/src/Bridges/ReflectionDI/ReflectionExtension.php',
|
||||
'Nette\\Bridges\\SecurityDI\\SecurityExtension' => $vendorDir . '/nette/security/src/Bridges/SecurityDI/SecurityExtension.php',
|
||||
'Nette\\Bridges\\SecurityTracy\\UserPanel' => $vendorDir . '/nette/security/src/Bridges/SecurityTracy/UserPanel.php',
|
||||
'Nette\\Caching\\Cache' => $vendorDir . '/nette/caching/src/Caching/Cache.php',
|
||||
'Nette\\Caching\\IBulkReader' => $vendorDir . '/nette/caching/src/Caching/IBulkReader.php',
|
||||
'Nette\\Caching\\IStorage' => $vendorDir . '/nette/caching/src/Caching/IStorage.php',
|
||||
'Nette\\Caching\\OutputHelper' => $vendorDir . '/nette/caching/src/Caching/OutputHelper.php',
|
||||
'Nette\\Caching\\Storages\\DevNullStorage' => $vendorDir . '/nette/caching/src/Caching/Storages/DevNullStorage.php',
|
||||
'Nette\\Caching\\Storages\\FileStorage' => $vendorDir . '/nette/caching/src/Caching/Storages/FileStorage.php',
|
||||
'Nette\\Caching\\Storages\\IJournal' => $vendorDir . '/nette/caching/src/Caching/Storages/IJournal.php',
|
||||
'Nette\\Caching\\Storages\\MemcachedStorage' => $vendorDir . '/nette/caching/src/Caching/Storages/MemcachedStorage.php',
|
||||
'Nette\\Caching\\Storages\\MemoryStorage' => $vendorDir . '/nette/caching/src/Caching/Storages/MemoryStorage.php',
|
||||
'Nette\\Caching\\Storages\\NewMemcachedStorage' => $vendorDir . '/nette/caching/src/Caching/Storages/NewMemcachedStorage.php',
|
||||
'Nette\\Caching\\Storages\\SQLiteJournal' => $vendorDir . '/nette/caching/src/Caching/Storages/SQLiteJournal.php',
|
||||
'Nette\\Caching\\Storages\\SQLiteStorage' => $vendorDir . '/nette/caching/src/Caching/Storages/SQLiteStorage.php',
|
||||
'Nette\\ComponentModel\\ArrayAccess' => $vendorDir . '/nette/component-model/src/ComponentModel/ArrayAccess.php',
|
||||
'Nette\\ComponentModel\\Component' => $vendorDir . '/nette/component-model/src/ComponentModel/Component.php',
|
||||
'Nette\\ComponentModel\\Container' => $vendorDir . '/nette/component-model/src/ComponentModel/Container.php',
|
||||
'Nette\\ComponentModel\\IComponent' => $vendorDir . '/nette/component-model/src/ComponentModel/IComponent.php',
|
||||
'Nette\\ComponentModel\\IContainer' => $vendorDir . '/nette/component-model/src/ComponentModel/IContainer.php',
|
||||
'Nette\\ComponentModel\\RecursiveComponentIterator' => $vendorDir . '/nette/component-model/src/ComponentModel/RecursiveComponentIterator.php',
|
||||
'Nette\\Configurator' => $vendorDir . '/nette/bootstrap/src/Bootstrap/Configurator.php',
|
||||
'Nette\\DI\\Compiler' => $vendorDir . '/nette/di/src/DI/Compiler.php',
|
||||
'Nette\\DI\\CompilerExtension' => $vendorDir . '/nette/di/src/DI/CompilerExtension.php',
|
||||
'Nette\\DI\\Config\\Adapters\\IniAdapter' => $vendorDir . '/nette/di/src/DI/Config/Adapters/IniAdapter.php',
|
||||
'Nette\\DI\\Config\\Adapters\\NeonAdapter' => $vendorDir . '/nette/di/src/DI/Config/Adapters/NeonAdapter.php',
|
||||
'Nette\\DI\\Config\\Adapters\\PhpAdapter' => $vendorDir . '/nette/di/src/DI/Config/Adapters/PhpAdapter.php',
|
||||
'Nette\\DI\\Config\\Helpers' => $vendorDir . '/nette/di/src/DI/Config/Helpers.php',
|
||||
'Nette\\DI\\Config\\IAdapter' => $vendorDir . '/nette/di/src/DI/Config/IAdapter.php',
|
||||
'Nette\\DI\\Config\\Loader' => $vendorDir . '/nette/di/src/DI/Config/Loader.php',
|
||||
'Nette\\DI\\Container' => $vendorDir . '/nette/di/src/DI/Container.php',
|
||||
'Nette\\DI\\ContainerBuilder' => $vendorDir . '/nette/di/src/DI/ContainerBuilder.php',
|
||||
'Nette\\DI\\ContainerLoader' => $vendorDir . '/nette/di/src/DI/ContainerLoader.php',
|
||||
'Nette\\DI\\DependencyChecker' => $vendorDir . '/nette/di/src/DI/DependencyChecker.php',
|
||||
'Nette\\DI\\Extensions\\ConstantsExtension' => $vendorDir . '/nette/di/src/DI/Extensions/ConstantsExtension.php',
|
||||
'Nette\\DI\\Extensions\\DIExtension' => $vendorDir . '/nette/di/src/DI/Extensions/DIExtension.php',
|
||||
'Nette\\DI\\Extensions\\DecoratorExtension' => $vendorDir . '/nette/di/src/DI/Extensions/DecoratorExtension.php',
|
||||
'Nette\\DI\\Extensions\\ExtensionsExtension' => $vendorDir . '/nette/di/src/DI/Extensions/ExtensionsExtension.php',
|
||||
'Nette\\DI\\Extensions\\InjectExtension' => $vendorDir . '/nette/di/src/DI/Extensions/InjectExtension.php',
|
||||
'Nette\\DI\\Extensions\\PhpExtension' => $vendorDir . '/nette/di/src/DI/Extensions/PhpExtension.php',
|
||||
'Nette\\DI\\Helpers' => $vendorDir . '/nette/di/src/DI/Helpers.php',
|
||||
'Nette\\DI\\MissingServiceException' => $vendorDir . '/nette/di/src/DI/exceptions.php',
|
||||
'Nette\\DI\\PhpGenerator' => $vendorDir . '/nette/di/src/DI/PhpGenerator.php',
|
||||
'Nette\\DI\\PhpReflection' => $vendorDir . '/nette/di/src/DI/PhpReflection.php',
|
||||
'Nette\\DI\\ServiceCreationException' => $vendorDir . '/nette/di/src/DI/exceptions.php',
|
||||
'Nette\\DI\\ServiceDefinition' => $vendorDir . '/nette/di/src/DI/ServiceDefinition.php',
|
||||
'Nette\\DI\\Statement' => $vendorDir . '/nette/di/src/DI/Statement.php',
|
||||
'Nette\\Database\\Connection' => $vendorDir . '/nette/database/src/Database/Connection.php',
|
||||
'Nette\\Database\\ConnectionException' => $vendorDir . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\ConstraintViolationException' => $vendorDir . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\Context' => $vendorDir . '/nette/database/src/Database/Context.php',
|
||||
'Nette\\Database\\Conventions\\AmbiguousReferenceKeyException' => $vendorDir . '/nette/database/src/Database/Conventions/AmbiguousReferenceKeyException.php',
|
||||
'Nette\\Database\\Conventions\\DiscoveredConventions' => $vendorDir . '/nette/database/src/Database/Conventions/DiscoveredConventions.php',
|
||||
'Nette\\Database\\Conventions\\StaticConventions' => $vendorDir . '/nette/database/src/Database/Conventions/StaticConventions.php',
|
||||
'Nette\\Database\\DriverException' => $vendorDir . '/nette/database/src/Database/DriverException.php',
|
||||
'Nette\\Database\\Drivers\\MsSqlDriver' => $vendorDir . '/nette/database/src/Database/Drivers/MsSqlDriver.php',
|
||||
'Nette\\Database\\Drivers\\MySqlDriver' => $vendorDir . '/nette/database/src/Database/Drivers/MySqlDriver.php',
|
||||
'Nette\\Database\\Drivers\\OciDriver' => $vendorDir . '/nette/database/src/Database/Drivers/OciDriver.php',
|
||||
'Nette\\Database\\Drivers\\OdbcDriver' => $vendorDir . '/nette/database/src/Database/Drivers/OdbcDriver.php',
|
||||
'Nette\\Database\\Drivers\\PgSqlDriver' => $vendorDir . '/nette/database/src/Database/Drivers/PgSqlDriver.php',
|
||||
'Nette\\Database\\Drivers\\Sqlite2Driver' => $vendorDir . '/nette/database/src/Database/Drivers/Sqlite2Driver.php',
|
||||
'Nette\\Database\\Drivers\\SqliteDriver' => $vendorDir . '/nette/database/src/Database/Drivers/SqliteDriver.php',
|
||||
'Nette\\Database\\Drivers\\SqlsrvDriver' => $vendorDir . '/nette/database/src/Database/Drivers/SqlsrvDriver.php',
|
||||
'Nette\\Database\\ForeignKeyConstraintViolationException' => $vendorDir . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\Helpers' => $vendorDir . '/nette/database/src/Database/Helpers.php',
|
||||
'Nette\\Database\\IConventions' => $vendorDir . '/nette/database/src/Database/IConventions.php',
|
||||
'Nette\\Database\\IRow' => $vendorDir . '/nette/database/src/Database/IRow.php',
|
||||
'Nette\\Database\\IRowContainer' => $vendorDir . '/nette/database/src/Database/IRowContainer.php',
|
||||
'Nette\\Database\\IStructure' => $vendorDir . '/nette/database/src/Database/IStructure.php',
|
||||
'Nette\\Database\\ISupplementalDriver' => $vendorDir . '/nette/database/src/Database/ISupplementalDriver.php',
|
||||
'Nette\\Database\\NotNullConstraintViolationException' => $vendorDir . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\ResultSet' => $vendorDir . '/nette/database/src/Database/ResultSet.php',
|
||||
'Nette\\Database\\Row' => $vendorDir . '/nette/database/src/Database/Row.php',
|
||||
'Nette\\Database\\SqlLiteral' => $vendorDir . '/nette/database/src/Database/SqlLiteral.php',
|
||||
'Nette\\Database\\SqlPreprocessor' => $vendorDir . '/nette/database/src/Database/SqlPreprocessor.php',
|
||||
'Nette\\Database\\Structure' => $vendorDir . '/nette/database/src/Database/Structure.php',
|
||||
'Nette\\Database\\Table\\ActiveRow' => $vendorDir . '/nette/database/src/Database/Table/ActiveRow.php',
|
||||
'Nette\\Database\\Table\\GroupedSelection' => $vendorDir . '/nette/database/src/Database/Table/GroupedSelection.php',
|
||||
'Nette\\Database\\Table\\IRow' => $vendorDir . '/nette/database/src/Database/Table/IRow.php',
|
||||
'Nette\\Database\\Table\\IRowContainer' => $vendorDir . '/nette/database/src/Database/Table/IRowContainer.php',
|
||||
'Nette\\Database\\Table\\Selection' => $vendorDir . '/nette/database/src/Database/Table/Selection.php',
|
||||
'Nette\\Database\\Table\\SqlBuilder' => $vendorDir . '/nette/database/src/Database/Table/SqlBuilder.php',
|
||||
'Nette\\Database\\UniqueConstraintViolationException' => $vendorDir . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\DeprecatedException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\DirectoryNotFoundException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\FileNotFoundException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Forms\\Container' => $vendorDir . '/nette/forms/src/Forms/Container.php',
|
||||
'Nette\\Forms\\ControlGroup' => $vendorDir . '/nette/forms/src/Forms/ControlGroup.php',
|
||||
'Nette\\Forms\\Controls\\BaseControl' => $vendorDir . '/nette/forms/src/Forms/Controls/BaseControl.php',
|
||||
'Nette\\Forms\\Controls\\Button' => $vendorDir . '/nette/forms/src/Forms/Controls/Button.php',
|
||||
'Nette\\Forms\\Controls\\Checkbox' => $vendorDir . '/nette/forms/src/Forms/Controls/Checkbox.php',
|
||||
'Nette\\Forms\\Controls\\CheckboxList' => $vendorDir . '/nette/forms/src/Forms/Controls/CheckboxList.php',
|
||||
'Nette\\Forms\\Controls\\ChoiceControl' => $vendorDir . '/nette/forms/src/Forms/Controls/ChoiceControl.php',
|
||||
'Nette\\Forms\\Controls\\CsrfProtection' => $vendorDir . '/nette/forms/src/Forms/Controls/CsrfProtection.php',
|
||||
'Nette\\Forms\\Controls\\HiddenField' => $vendorDir . '/nette/forms/src/Forms/Controls/HiddenField.php',
|
||||
'Nette\\Forms\\Controls\\ImageButton' => $vendorDir . '/nette/forms/src/Forms/Controls/ImageButton.php',
|
||||
'Nette\\Forms\\Controls\\MultiChoiceControl' => $vendorDir . '/nette/forms/src/Forms/Controls/MultiChoiceControl.php',
|
||||
'Nette\\Forms\\Controls\\MultiSelectBox' => $vendorDir . '/nette/forms/src/Forms/Controls/MultiSelectBox.php',
|
||||
'Nette\\Forms\\Controls\\RadioList' => $vendorDir . '/nette/forms/src/Forms/Controls/RadioList.php',
|
||||
'Nette\\Forms\\Controls\\SelectBox' => $vendorDir . '/nette/forms/src/Forms/Controls/SelectBox.php',
|
||||
'Nette\\Forms\\Controls\\SubmitButton' => $vendorDir . '/nette/forms/src/Forms/Controls/SubmitButton.php',
|
||||
'Nette\\Forms\\Controls\\TextArea' => $vendorDir . '/nette/forms/src/Forms/Controls/TextArea.php',
|
||||
'Nette\\Forms\\Controls\\TextBase' => $vendorDir . '/nette/forms/src/Forms/Controls/TextBase.php',
|
||||
'Nette\\Forms\\Controls\\TextInput' => $vendorDir . '/nette/forms/src/Forms/Controls/TextInput.php',
|
||||
'Nette\\Forms\\Controls\\UploadControl' => $vendorDir . '/nette/forms/src/Forms/Controls/UploadControl.php',
|
||||
'Nette\\Forms\\Form' => $vendorDir . '/nette/forms/src/Forms/Form.php',
|
||||
'Nette\\Forms\\Helpers' => $vendorDir . '/nette/forms/src/Forms/Helpers.php',
|
||||
'Nette\\Forms\\IControl' => $vendorDir . '/nette/forms/src/Forms/IControl.php',
|
||||
'Nette\\Forms\\IFormRenderer' => $vendorDir . '/nette/forms/src/Forms/IFormRenderer.php',
|
||||
'Nette\\Forms\\ISubmitterControl' => $vendorDir . '/nette/forms/src/Forms/ISubmitterControl.php',
|
||||
'Nette\\Forms\\Rendering\\DefaultFormRenderer' => $vendorDir . '/nette/forms/src/Forms/Rendering/DefaultFormRenderer.php',
|
||||
'Nette\\Forms\\Rule' => $vendorDir . '/nette/forms/src/Forms/Rule.php',
|
||||
'Nette\\Forms\\Rules' => $vendorDir . '/nette/forms/src/Forms/Rules.php',
|
||||
'Nette\\Forms\\Validator' => $vendorDir . '/nette/forms/src/Forms/Validator.php',
|
||||
'Nette\\Http\\Context' => $vendorDir . '/nette/http/src/Http/Context.php',
|
||||
'Nette\\Http\\FileUpload' => $vendorDir . '/nette/http/src/Http/FileUpload.php',
|
||||
'Nette\\Http\\Helpers' => $vendorDir . '/nette/http/src/Http/Helpers.php',
|
||||
'Nette\\Http\\IRequest' => $vendorDir . '/nette/http/src/Http/IRequest.php',
|
||||
'Nette\\Http\\IResponse' => $vendorDir . '/nette/http/src/Http/IResponse.php',
|
||||
'Nette\\Http\\ISessionStorage' => $vendorDir . '/nette/http/src/Http/ISessionStorage.php',
|
||||
'Nette\\Http\\Request' => $vendorDir . '/nette/http/src/Http/Request.php',
|
||||
'Nette\\Http\\RequestFactory' => $vendorDir . '/nette/http/src/Http/RequestFactory.php',
|
||||
'Nette\\Http\\Response' => $vendorDir . '/nette/http/src/Http/Response.php',
|
||||
'Nette\\Http\\Session' => $vendorDir . '/nette/http/src/Http/Session.php',
|
||||
'Nette\\Http\\SessionSection' => $vendorDir . '/nette/http/src/Http/SessionSection.php',
|
||||
'Nette\\Http\\Url' => $vendorDir . '/nette/http/src/Http/Url.php',
|
||||
'Nette\\Http\\UrlScript' => $vendorDir . '/nette/http/src/Http/UrlScript.php',
|
||||
'Nette\\Http\\UserStorage' => $vendorDir . '/nette/http/src/Http/UserStorage.php',
|
||||
'Nette\\IOException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\InvalidArgumentException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\InvalidStateException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Iterators\\CachingIterator' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php',
|
||||
'Nette\\Iterators\\Mapper' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php',
|
||||
'Nette\\LegacyObject' => $vendorDir . '/nette/utils/src/Utils/LegacyObject.php',
|
||||
'Nette\\Loaders\\RobotLoader' => $vendorDir . '/nette/robot-loader/src/RobotLoader/RobotLoader.php',
|
||||
'Nette\\Localization\\ITranslator' => $vendorDir . '/nette/utils/src/Utils/ITranslator.php',
|
||||
'Nette\\Mail\\FallbackMailer' => $vendorDir . '/nette/mail/src/Mail/FallbackMailer.php',
|
||||
'Nette\\Mail\\FallbackMailerException' => $vendorDir . '/nette/mail/src/Mail/exceptions.php',
|
||||
'Nette\\Mail\\IMailer' => $vendorDir . '/nette/mail/src/Mail/IMailer.php',
|
||||
'Nette\\Mail\\Message' => $vendorDir . '/nette/mail/src/Mail/Message.php',
|
||||
'Nette\\Mail\\MimePart' => $vendorDir . '/nette/mail/src/Mail/MimePart.php',
|
||||
'Nette\\Mail\\SendException' => $vendorDir . '/nette/mail/src/Mail/exceptions.php',
|
||||
'Nette\\Mail\\SendmailMailer' => $vendorDir . '/nette/mail/src/Mail/SendmailMailer.php',
|
||||
'Nette\\Mail\\SmtpException' => $vendorDir . '/nette/mail/src/Mail/exceptions.php',
|
||||
'Nette\\Mail\\SmtpMailer' => $vendorDir . '/nette/mail/src/Mail/SmtpMailer.php',
|
||||
'Nette\\MemberAccessException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Neon\\Decoder' => $vendorDir . '/nette/neon/src/Neon/Decoder.php',
|
||||
'Nette\\Neon\\Encoder' => $vendorDir . '/nette/neon/src/Neon/Encoder.php',
|
||||
'Nette\\Neon\\Entity' => $vendorDir . '/nette/neon/src/Neon/Entity.php',
|
||||
'Nette\\Neon\\Exception' => $vendorDir . '/nette/neon/src/Neon/Exception.php',
|
||||
'Nette\\Neon\\Neon' => $vendorDir . '/nette/neon/src/Neon/Neon.php',
|
||||
'Nette\\NotImplementedException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\NotSupportedException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\OutOfRangeException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\PhpGenerator\\ClassType' => $vendorDir . '/nette/php-generator/src/PhpGenerator/ClassType.php',
|
||||
'Nette\\PhpGenerator\\Closure' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Closure.php',
|
||||
'Nette\\PhpGenerator\\Constant' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Constant.php',
|
||||
'Nette\\PhpGenerator\\Factory' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Factory.php',
|
||||
'Nette\\PhpGenerator\\GlobalFunction' => $vendorDir . '/nette/php-generator/src/PhpGenerator/GlobalFunction.php',
|
||||
'Nette\\PhpGenerator\\Helpers' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Helpers.php',
|
||||
'Nette\\PhpGenerator\\Method' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Method.php',
|
||||
'Nette\\PhpGenerator\\Parameter' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Parameter.php',
|
||||
'Nette\\PhpGenerator\\PhpFile' => $vendorDir . '/nette/php-generator/src/PhpGenerator/PhpFile.php',
|
||||
'Nette\\PhpGenerator\\PhpLiteral' => $vendorDir . '/nette/php-generator/src/PhpGenerator/PhpLiteral.php',
|
||||
'Nette\\PhpGenerator\\PhpNamespace' => $vendorDir . '/nette/php-generator/src/PhpGenerator/PhpNamespace.php',
|
||||
'Nette\\PhpGenerator\\Printer' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Printer.php',
|
||||
'Nette\\PhpGenerator\\Property' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Property.php',
|
||||
'Nette\\PhpGenerator\\PsrPrinter' => $vendorDir . '/nette/php-generator/src/PhpGenerator/PsrPrinter.php',
|
||||
'Nette\\PhpGenerator\\Traits\\CommentAware' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Traits/CommentAware.php',
|
||||
'Nette\\PhpGenerator\\Traits\\FunctionLike' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Traits/FunctionLike.php',
|
||||
'Nette\\PhpGenerator\\Traits\\NameAware' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Traits/NameAware.php',
|
||||
'Nette\\PhpGenerator\\Traits\\VisibilityAware' => $vendorDir . '/nette/php-generator/src/PhpGenerator/Traits/VisibilityAware.php',
|
||||
'Nette\\Reflection\\Annotation' => $vendorDir . '/nette/reflection/src/Reflection/Annotation.php',
|
||||
'Nette\\Reflection\\AnnotationsParser' => $vendorDir . '/nette/reflection/src/Reflection/AnnotationsParser.php',
|
||||
'Nette\\Reflection\\ClassType' => $vendorDir . '/nette/reflection/src/Reflection/ClassType.php',
|
||||
'Nette\\Reflection\\Extension' => $vendorDir . '/nette/reflection/src/Reflection/Extension.php',
|
||||
'Nette\\Reflection\\GlobalFunction' => $vendorDir . '/nette/reflection/src/Reflection/GlobalFunction.php',
|
||||
'Nette\\Reflection\\Helpers' => $vendorDir . '/nette/reflection/src/Reflection/Helpers.php',
|
||||
'Nette\\Reflection\\IAnnotation' => $vendorDir . '/nette/reflection/src/Reflection/IAnnotation.php',
|
||||
'Nette\\Reflection\\Method' => $vendorDir . '/nette/reflection/src/Reflection/Method.php',
|
||||
'Nette\\Reflection\\Parameter' => $vendorDir . '/nette/reflection/src/Reflection/Parameter.php',
|
||||
'Nette\\Reflection\\Property' => $vendorDir . '/nette/reflection/src/Reflection/Property.php',
|
||||
'Nette\\Security\\AuthenticationException' => $vendorDir . '/nette/security/src/Security/AuthenticationException.php',
|
||||
'Nette\\Security\\IAuthenticator' => $vendorDir . '/nette/security/src/Security/IAuthenticator.php',
|
||||
'Nette\\Security\\IAuthorizator' => $vendorDir . '/nette/security/src/Security/IAuthorizator.php',
|
||||
'Nette\\Security\\IIdentity' => $vendorDir . '/nette/security/src/Security/IIdentity.php',
|
||||
'Nette\\Security\\IResource' => $vendorDir . '/nette/security/src/Security/IResource.php',
|
||||
'Nette\\Security\\IRole' => $vendorDir . '/nette/security/src/Security/IRole.php',
|
||||
'Nette\\Security\\IUserStorage' => $vendorDir . '/nette/security/src/Security/IUserStorage.php',
|
||||
'Nette\\Security\\Identity' => $vendorDir . '/nette/security/src/Security/Identity.php',
|
||||
'Nette\\Security\\Passwords' => $vendorDir . '/nette/security/src/Security/Passwords.php',
|
||||
'Nette\\Security\\Permission' => $vendorDir . '/nette/security/src/Security/Permission.php',
|
||||
'Nette\\Security\\SimpleAuthenticator' => $vendorDir . '/nette/security/src/Security/SimpleAuthenticator.php',
|
||||
'Nette\\Security\\User' => $vendorDir . '/nette/security/src/Security/User.php',
|
||||
'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/Utils/SmartObject.php',
|
||||
'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/Utils/StaticClass.php',
|
||||
'Nette\\StaticClassException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ArrayHash' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php',
|
||||
'Nette\\Utils\\ArrayList' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php',
|
||||
'Nette\\Utils\\Arrays' => $vendorDir . '/nette/utils/src/Utils/Arrays.php',
|
||||
'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php',
|
||||
'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php',
|
||||
'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php',
|
||||
'Nette\\Utils\\Finder' => $vendorDir . '/nette/finder/src/Utils/Finder.php',
|
||||
'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php',
|
||||
'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/Utils/IHtmlString.php',
|
||||
'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php',
|
||||
'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php',
|
||||
'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php',
|
||||
'Nette\\Utils\\ObjectMixin' => $vendorDir . '/nette/utils/src/Utils/ObjectMixin.php',
|
||||
'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php',
|
||||
'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php',
|
||||
'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php',
|
||||
'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php',
|
||||
'Nette\\Utils\\UnknownImageFileException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Validators' => $vendorDir . '/nette/utils/src/Utils/Validators.php',
|
||||
'Tester\\Assert' => $vendorDir . '/nette/tester/src/Framework/Assert.php',
|
||||
'Tester\\AssertException' => $vendorDir . '/nette/tester/src/Framework/AssertException.php',
|
||||
'Tester\\CodeCoverage\\Collector' => $vendorDir . '/nette/tester/src/CodeCoverage/Collector.php',
|
||||
'Tester\\CodeCoverage\\Generators\\AbstractGenerator' => $vendorDir . '/nette/tester/src/CodeCoverage/Generators/AbstractGenerator.php',
|
||||
'Tester\\CodeCoverage\\Generators\\CloverXMLGenerator' => $vendorDir . '/nette/tester/src/CodeCoverage/Generators/CloverXMLGenerator.php',
|
||||
'Tester\\CodeCoverage\\Generators\\HtmlGenerator' => $vendorDir . '/nette/tester/src/CodeCoverage/Generators/HtmlGenerator.php',
|
||||
'Tester\\CodeCoverage\\PhpParser' => $vendorDir . '/nette/tester/src/CodeCoverage/PhpParser.php',
|
||||
'Tester\\DataProvider' => $vendorDir . '/nette/tester/src/Framework/DataProvider.php',
|
||||
'Tester\\DomQuery' => $vendorDir . '/nette/tester/src/Framework/DomQuery.php',
|
||||
'Tester\\Dumper' => $vendorDir . '/nette/tester/src/Framework/Dumper.php',
|
||||
'Tester\\Environment' => $vendorDir . '/nette/tester/src/Framework/Environment.php',
|
||||
'Tester\\Expect' => $vendorDir . '/nette/tester/src/Framework/Expect.php',
|
||||
'Tester\\FileMock' => $vendorDir . '/nette/tester/src/Framework/FileMock.php',
|
||||
'Tester\\FileMutator' => $vendorDir . '/nette/tester/src/Framework/FileMutator.php',
|
||||
'Tester\\Helpers' => $vendorDir . '/nette/tester/src/Framework/Helpers.php',
|
||||
'Tester\\Runner\\CliTester' => $vendorDir . '/nette/tester/src/Runner/CliTester.php',
|
||||
'Tester\\Runner\\CommandLine' => $vendorDir . '/nette/tester/src/Runner/CommandLine.php',
|
||||
'Tester\\Runner\\Job' => $vendorDir . '/nette/tester/src/Runner/Job.php',
|
||||
'Tester\\Runner\\OutputHandler' => $vendorDir . '/nette/tester/src/Runner/OutputHandler.php',
|
||||
'Tester\\Runner\\Output\\ConsolePrinter' => $vendorDir . '/nette/tester/src/Runner/Output/ConsolePrinter.php',
|
||||
'Tester\\Runner\\Output\\JUnitPrinter' => $vendorDir . '/nette/tester/src/Runner/Output/JUnitPrinter.php',
|
||||
'Tester\\Runner\\Output\\Logger' => $vendorDir . '/nette/tester/src/Runner/Output/Logger.php',
|
||||
'Tester\\Runner\\Output\\TapPrinter' => $vendorDir . '/nette/tester/src/Runner/Output/TapPrinter.php',
|
||||
'Tester\\Runner\\PhpInterpreter' => $vendorDir . '/nette/tester/src/Runner/PhpInterpreter.php',
|
||||
'Tester\\Runner\\Runner' => $vendorDir . '/nette/tester/src/Runner/Runner.php',
|
||||
'Tester\\Runner\\Test' => $vendorDir . '/nette/tester/src/Runner/Test.php',
|
||||
'Tester\\Runner\\TestHandler' => $vendorDir . '/nette/tester/src/Runner/TestHandler.php',
|
||||
'Tester\\TestCase' => $vendorDir . '/nette/tester/src/Framework/TestCase.php',
|
||||
'Tester\\TestCaseException' => $vendorDir . '/nette/tester/src/Framework/TestCase.php',
|
||||
'Tracy\\Bar' => $vendorDir . '/tracy/tracy/src/Tracy/Bar/Bar.php',
|
||||
'Tracy\\BlueScreen' => $vendorDir . '/tracy/tracy/src/Tracy/BlueScreen/BlueScreen.php',
|
||||
'Tracy\\Bridges\\Nette\\Bridge' => $vendorDir . '/tracy/tracy/src/Bridges/Nette/Bridge.php',
|
||||
'Tracy\\Bridges\\Nette\\MailSender' => $vendorDir . '/tracy/tracy/src/Bridges/Nette/MailSender.php',
|
||||
'Tracy\\Bridges\\Nette\\TracyExtension' => $vendorDir . '/tracy/tracy/src/Bridges/Nette/TracyExtension.php',
|
||||
'Tracy\\Bridges\\Psr\\PsrToTracyLoggerAdapter' => $vendorDir . '/tracy/tracy/src/Bridges/Psr/PsrToTracyLoggerAdapter.php',
|
||||
'Tracy\\Bridges\\Psr\\TracyToPsrLoggerAdapter' => $vendorDir . '/tracy/tracy/src/Bridges/Psr/TracyToPsrLoggerAdapter.php',
|
||||
'Tracy\\Debugger' => $vendorDir . '/tracy/tracy/src/Tracy/Debugger/Debugger.php',
|
||||
'Tracy\\DefaultBarPanel' => $vendorDir . '/tracy/tracy/src/Tracy/Bar/DefaultBarPanel.php',
|
||||
'Tracy\\Dumper' => $vendorDir . '/tracy/tracy/src/Tracy/Dumper/Dumper.php',
|
||||
'Tracy\\FireLogger' => $vendorDir . '/tracy/tracy/src/Tracy/Logger/FireLogger.php',
|
||||
'Tracy\\Helpers' => $vendorDir . '/tracy/tracy/src/Tracy/Helpers.php',
|
||||
'Tracy\\IBarPanel' => $vendorDir . '/tracy/tracy/src/Tracy/Bar/IBarPanel.php',
|
||||
'Tracy\\ILogger' => $vendorDir . '/tracy/tracy/src/Tracy/Logger/ILogger.php',
|
||||
'Tracy\\Logger' => $vendorDir . '/tracy/tracy/src/Tracy/Logger/Logger.php',
|
||||
'Tracy\\OutputDebugger' => $vendorDir . '/tracy/tracy/src/Tracy/OutputDebugger/OutputDebugger.php',
|
||||
);
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'19a7e2c3b1d506dcdc1b60aab8e102e4' => $vendorDir . '/tracy/tracy/src/Tracy/shortcuts.php',
|
||||
'a12da592622097d2b593a430e32e13fd' => $vendorDir . '/nette/utils/src/loader.php',
|
||||
'e7a6fd2e7a869bb207158e9416bdd9b0' => $vendorDir . '/nette/application/src/compatibility.php',
|
||||
);
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit1e4e7be4b54097efddcd9fd472eda330
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit1e4e7be4b54097efddcd9fd472eda330', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit1e4e7be4b54097efddcd9fd472eda330', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit1e4e7be4b54097efddcd9fd472eda330::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit1e4e7be4b54097efddcd9fd472eda330::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire1e4e7be4b54097efddcd9fd472eda330($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire1e4e7be4b54097efddcd9fd472eda330($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
Vendored
+390
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit1e4e7be4b54097efddcd9fd472eda330
|
||||
{
|
||||
public static $files = array (
|
||||
'19a7e2c3b1d506dcdc1b60aab8e102e4' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/shortcuts.php',
|
||||
'a12da592622097d2b593a430e32e13fd' => __DIR__ . '/..' . '/nette/utils/src/loader.php',
|
||||
'e7a6fd2e7a869bb207158e9416bdd9b0' => __DIR__ . '/..' . '/nette/application/src/compatibility.php',
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Latte\\CompileException' => __DIR__ . '/..' . '/latte/latte/src/Latte/exceptions.php',
|
||||
'Latte\\Compiler' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/Compiler.php',
|
||||
'Latte\\Engine' => __DIR__ . '/..' . '/latte/latte/src/Latte/Engine.php',
|
||||
'Latte\\Helpers' => __DIR__ . '/..' . '/latte/latte/src/Latte/Helpers.php',
|
||||
'Latte\\HtmlNode' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/HtmlNode.php',
|
||||
'Latte\\ILoader' => __DIR__ . '/..' . '/latte/latte/src/Latte/ILoader.php',
|
||||
'Latte\\IMacro' => __DIR__ . '/..' . '/latte/latte/src/Latte/IMacro.php',
|
||||
'Latte\\Loaders\\FileLoader' => __DIR__ . '/..' . '/latte/latte/src/Latte/Loaders/FileLoader.php',
|
||||
'Latte\\Loaders\\StringLoader' => __DIR__ . '/..' . '/latte/latte/src/Latte/Loaders/StringLoader.php',
|
||||
'Latte\\MacroNode' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/MacroNode.php',
|
||||
'Latte\\MacroTokens' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/MacroTokens.php',
|
||||
'Latte\\Macros\\BlockMacros' => __DIR__ . '/..' . '/latte/latte/src/Latte/Macros/BlockMacros.php',
|
||||
'Latte\\Macros\\CoreMacros' => __DIR__ . '/..' . '/latte/latte/src/Latte/Macros/CoreMacros.php',
|
||||
'Latte\\Macros\\MacroSet' => __DIR__ . '/..' . '/latte/latte/src/Latte/Macros/MacroSet.php',
|
||||
'Latte\\Parser' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/Parser.php',
|
||||
'Latte\\PhpHelpers' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/PhpHelpers.php',
|
||||
'Latte\\PhpWriter' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/PhpWriter.php',
|
||||
'Latte\\RegexpException' => __DIR__ . '/..' . '/latte/latte/src/Latte/exceptions.php',
|
||||
'Latte\\RuntimeException' => __DIR__ . '/..' . '/latte/latte/src/Latte/exceptions.php',
|
||||
'Latte\\Runtime\\CachingIterator' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/CachingIterator.php',
|
||||
'Latte\\Runtime\\FilterExecutor' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/FilterExecutor.php',
|
||||
'Latte\\Runtime\\FilterInfo' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/FilterInfo.php',
|
||||
'Latte\\Runtime\\Filters' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/Filters.php',
|
||||
'Latte\\Runtime\\Html' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/Html.php',
|
||||
'Latte\\Runtime\\IHtmlString' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/IHtmlString.php',
|
||||
'Latte\\Runtime\\ISnippetBridge' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/ISnippetBridge.php',
|
||||
'Latte\\Runtime\\SnippetDriver' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/SnippetDriver.php',
|
||||
'Latte\\Runtime\\Template' => __DIR__ . '/..' . '/latte/latte/src/Latte/Runtime/Template.php',
|
||||
'Latte\\Strict' => __DIR__ . '/..' . '/latte/latte/src/Latte/Strict.php',
|
||||
'Latte\\Token' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/Token.php',
|
||||
'Latte\\TokenIterator' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/TokenIterator.php',
|
||||
'Latte\\Tokenizer' => __DIR__ . '/..' . '/latte/latte/src/Latte/Compiler/Tokenizer.php',
|
||||
'NetteModule\\ErrorPresenter' => __DIR__ . '/..' . '/nette/application/src/Application/ErrorPresenter.php',
|
||||
'NetteModule\\MicroPresenter' => __DIR__ . '/..' . '/nette/application/src/Application/MicroPresenter.php',
|
||||
'Nette\\Application\\AbortException' => __DIR__ . '/..' . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\Application' => __DIR__ . '/..' . '/nette/application/src/Application/Application.php',
|
||||
'Nette\\Application\\ApplicationException' => __DIR__ . '/..' . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\BadRequestException' => __DIR__ . '/..' . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\ForbiddenRequestException' => __DIR__ . '/..' . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\Helpers' => __DIR__ . '/..' . '/nette/application/src/Application/Helpers.php',
|
||||
'Nette\\Application\\IPresenter' => __DIR__ . '/..' . '/nette/application/src/Application/IPresenter.php',
|
||||
'Nette\\Application\\IPresenterFactory' => __DIR__ . '/..' . '/nette/application/src/Application/IPresenterFactory.php',
|
||||
'Nette\\Application\\IResponse' => __DIR__ . '/..' . '/nette/application/src/Application/IResponse.php',
|
||||
'Nette\\Application\\IRouter' => __DIR__ . '/..' . '/nette/application/src/Application/IRouter.php',
|
||||
'Nette\\Application\\InvalidPresenterException' => __DIR__ . '/..' . '/nette/application/src/Application/exceptions.php',
|
||||
'Nette\\Application\\LinkGenerator' => __DIR__ . '/..' . '/nette/application/src/Application/LinkGenerator.php',
|
||||
'Nette\\Application\\PresenterFactory' => __DIR__ . '/..' . '/nette/application/src/Application/PresenterFactory.php',
|
||||
'Nette\\Application\\Request' => __DIR__ . '/..' . '/nette/application/src/Application/Request.php',
|
||||
'Nette\\Application\\Responses\\CallbackResponse' => __DIR__ . '/..' . '/nette/application/src/Application/Responses/CallbackResponse.php',
|
||||
'Nette\\Application\\Responses\\FileResponse' => __DIR__ . '/..' . '/nette/application/src/Application/Responses/FileResponse.php',
|
||||
'Nette\\Application\\Responses\\ForwardResponse' => __DIR__ . '/..' . '/nette/application/src/Application/Responses/ForwardResponse.php',
|
||||
'Nette\\Application\\Responses\\JsonResponse' => __DIR__ . '/..' . '/nette/application/src/Application/Responses/JsonResponse.php',
|
||||
'Nette\\Application\\Responses\\RedirectResponse' => __DIR__ . '/..' . '/nette/application/src/Application/Responses/RedirectResponse.php',
|
||||
'Nette\\Application\\Responses\\TextResponse' => __DIR__ . '/..' . '/nette/application/src/Application/Responses/TextResponse.php',
|
||||
'Nette\\Application\\Routers\\CliRouter' => __DIR__ . '/..' . '/nette/application/src/Application/Routers/CliRouter.php',
|
||||
'Nette\\Application\\Routers\\Route' => __DIR__ . '/..' . '/nette/application/src/Application/Routers/Route.php',
|
||||
'Nette\\Application\\Routers\\RouteList' => __DIR__ . '/..' . '/nette/application/src/Application/Routers/RouteList.php',
|
||||
'Nette\\Application\\Routers\\SimpleRouter' => __DIR__ . '/..' . '/nette/application/src/Application/Routers/SimpleRouter.php',
|
||||
'Nette\\Application\\UI\\BadSignalException' => __DIR__ . '/..' . '/nette/application/src/Application/UI/BadSignalException.php',
|
||||
'Nette\\Application\\UI\\Component' => __DIR__ . '/..' . '/nette/application/src/Application/UI/Component.php',
|
||||
'Nette\\Application\\UI\\ComponentReflection' => __DIR__ . '/..' . '/nette/application/src/Application/UI/ComponentReflection.php',
|
||||
'Nette\\Application\\UI\\Control' => __DIR__ . '/..' . '/nette/application/src/Application/UI/Control.php',
|
||||
'Nette\\Application\\UI\\Form' => __DIR__ . '/..' . '/nette/application/src/Application/UI/Form.php',
|
||||
'Nette\\Application\\UI\\IRenderable' => __DIR__ . '/..' . '/nette/application/src/Application/UI/IRenderable.php',
|
||||
'Nette\\Application\\UI\\ISignalReceiver' => __DIR__ . '/..' . '/nette/application/src/Application/UI/ISignalReceiver.php',
|
||||
'Nette\\Application\\UI\\IStatePersistent' => __DIR__ . '/..' . '/nette/application/src/Application/UI/IStatePersistent.php',
|
||||
'Nette\\Application\\UI\\ITemplate' => __DIR__ . '/..' . '/nette/application/src/Application/UI/ITemplate.php',
|
||||
'Nette\\Application\\UI\\ITemplateFactory' => __DIR__ . '/..' . '/nette/application/src/Application/UI/ITemplateFactory.php',
|
||||
'Nette\\Application\\UI\\InvalidLinkException' => __DIR__ . '/..' . '/nette/application/src/Application/UI/InvalidLinkException.php',
|
||||
'Nette\\Application\\UI\\Link' => __DIR__ . '/..' . '/nette/application/src/Application/UI/Link.php',
|
||||
'Nette\\Application\\UI\\MethodReflection' => __DIR__ . '/..' . '/nette/application/src/Application/UI/MethodReflection.php',
|
||||
'Nette\\Application\\UI\\Multiplier' => __DIR__ . '/..' . '/nette/application/src/Application/UI/Multiplier.php',
|
||||
'Nette\\Application\\UI\\Presenter' => __DIR__ . '/..' . '/nette/application/src/Application/UI/Presenter.php',
|
||||
'Nette\\ArgumentOutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\ApplicationExtension' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationDI/ApplicationExtension.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\LatteExtension' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationDI/LatteExtension.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\PresenterFactoryCallback' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationDI/PresenterFactoryCallback.php',
|
||||
'Nette\\Bridges\\ApplicationDI\\RoutingExtension' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationDI/RoutingExtension.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\ILatteFactory' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/ILatteFactory.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\Loader' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/Loader.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\SnippetBridge' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/SnippetBridge.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\Template' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/Template.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\TemplateFactory' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/TemplateFactory.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\UIMacros' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/UIMacros.php',
|
||||
'Nette\\Bridges\\ApplicationLatte\\UIRuntime' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationLatte/UIRuntime.php',
|
||||
'Nette\\Bridges\\ApplicationTracy\\RoutingPanel' => __DIR__ . '/..' . '/nette/application/src/Bridges/ApplicationTracy/RoutingPanel.php',
|
||||
'Nette\\Bridges\\CacheDI\\CacheExtension' => __DIR__ . '/..' . '/nette/caching/src/Bridges/CacheDI/CacheExtension.php',
|
||||
'Nette\\Bridges\\CacheLatte\\CacheMacro' => __DIR__ . '/..' . '/nette/caching/src/Bridges/CacheLatte/CacheMacro.php',
|
||||
'Nette\\Bridges\\DITracy\\ContainerPanel' => __DIR__ . '/..' . '/nette/di/src/Bridges/DITracy/ContainerPanel.php',
|
||||
'Nette\\Bridges\\DatabaseDI\\DatabaseExtension' => __DIR__ . '/..' . '/nette/database/src/Bridges/DatabaseDI/DatabaseExtension.php',
|
||||
'Nette\\Bridges\\DatabaseTracy\\ConnectionPanel' => __DIR__ . '/..' . '/nette/database/src/Bridges/DatabaseTracy/ConnectionPanel.php',
|
||||
'Nette\\Bridges\\FormsDI\\FormsExtension' => __DIR__ . '/..' . '/nette/forms/src/Bridges/FormsDI/FormsExtension.php',
|
||||
'Nette\\Bridges\\FormsLatte\\FormMacros' => __DIR__ . '/..' . '/nette/forms/src/Bridges/FormsLatte/FormMacros.php',
|
||||
'Nette\\Bridges\\FormsLatte\\Runtime' => __DIR__ . '/..' . '/nette/forms/src/Bridges/FormsLatte/Runtime.php',
|
||||
'Nette\\Bridges\\Framework\\TracyBridge' => __DIR__ . '/..' . '/nette/bootstrap/src/Bridges/Framework/TracyBridge.php',
|
||||
'Nette\\Bridges\\HttpDI\\HttpExtension' => __DIR__ . '/..' . '/nette/http/src/Bridges/HttpDI/HttpExtension.php',
|
||||
'Nette\\Bridges\\HttpDI\\SessionExtension' => __DIR__ . '/..' . '/nette/http/src/Bridges/HttpDI/SessionExtension.php',
|
||||
'Nette\\Bridges\\HttpTracy\\SessionPanel' => __DIR__ . '/..' . '/nette/http/src/Bridges/HttpTracy/SessionPanel.php',
|
||||
'Nette\\Bridges\\MailDI\\MailExtension' => __DIR__ . '/..' . '/nette/mail/src/Bridges/MailDI/MailExtension.php',
|
||||
'Nette\\Bridges\\ReflectionDI\\ReflectionExtension' => __DIR__ . '/..' . '/nette/reflection/src/Bridges/ReflectionDI/ReflectionExtension.php',
|
||||
'Nette\\Bridges\\SecurityDI\\SecurityExtension' => __DIR__ . '/..' . '/nette/security/src/Bridges/SecurityDI/SecurityExtension.php',
|
||||
'Nette\\Bridges\\SecurityTracy\\UserPanel' => __DIR__ . '/..' . '/nette/security/src/Bridges/SecurityTracy/UserPanel.php',
|
||||
'Nette\\Caching\\Cache' => __DIR__ . '/..' . '/nette/caching/src/Caching/Cache.php',
|
||||
'Nette\\Caching\\IBulkReader' => __DIR__ . '/..' . '/nette/caching/src/Caching/IBulkReader.php',
|
||||
'Nette\\Caching\\IStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/IStorage.php',
|
||||
'Nette\\Caching\\OutputHelper' => __DIR__ . '/..' . '/nette/caching/src/Caching/OutputHelper.php',
|
||||
'Nette\\Caching\\Storages\\DevNullStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/DevNullStorage.php',
|
||||
'Nette\\Caching\\Storages\\FileStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/FileStorage.php',
|
||||
'Nette\\Caching\\Storages\\IJournal' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/IJournal.php',
|
||||
'Nette\\Caching\\Storages\\MemcachedStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/MemcachedStorage.php',
|
||||
'Nette\\Caching\\Storages\\MemoryStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/MemoryStorage.php',
|
||||
'Nette\\Caching\\Storages\\NewMemcachedStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/NewMemcachedStorage.php',
|
||||
'Nette\\Caching\\Storages\\SQLiteJournal' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/SQLiteJournal.php',
|
||||
'Nette\\Caching\\Storages\\SQLiteStorage' => __DIR__ . '/..' . '/nette/caching/src/Caching/Storages/SQLiteStorage.php',
|
||||
'Nette\\ComponentModel\\ArrayAccess' => __DIR__ . '/..' . '/nette/component-model/src/ComponentModel/ArrayAccess.php',
|
||||
'Nette\\ComponentModel\\Component' => __DIR__ . '/..' . '/nette/component-model/src/ComponentModel/Component.php',
|
||||
'Nette\\ComponentModel\\Container' => __DIR__ . '/..' . '/nette/component-model/src/ComponentModel/Container.php',
|
||||
'Nette\\ComponentModel\\IComponent' => __DIR__ . '/..' . '/nette/component-model/src/ComponentModel/IComponent.php',
|
||||
'Nette\\ComponentModel\\IContainer' => __DIR__ . '/..' . '/nette/component-model/src/ComponentModel/IContainer.php',
|
||||
'Nette\\ComponentModel\\RecursiveComponentIterator' => __DIR__ . '/..' . '/nette/component-model/src/ComponentModel/RecursiveComponentIterator.php',
|
||||
'Nette\\Configurator' => __DIR__ . '/..' . '/nette/bootstrap/src/Bootstrap/Configurator.php',
|
||||
'Nette\\DI\\Compiler' => __DIR__ . '/..' . '/nette/di/src/DI/Compiler.php',
|
||||
'Nette\\DI\\CompilerExtension' => __DIR__ . '/..' . '/nette/di/src/DI/CompilerExtension.php',
|
||||
'Nette\\DI\\Config\\Adapters\\IniAdapter' => __DIR__ . '/..' . '/nette/di/src/DI/Config/Adapters/IniAdapter.php',
|
||||
'Nette\\DI\\Config\\Adapters\\NeonAdapter' => __DIR__ . '/..' . '/nette/di/src/DI/Config/Adapters/NeonAdapter.php',
|
||||
'Nette\\DI\\Config\\Adapters\\PhpAdapter' => __DIR__ . '/..' . '/nette/di/src/DI/Config/Adapters/PhpAdapter.php',
|
||||
'Nette\\DI\\Config\\Helpers' => __DIR__ . '/..' . '/nette/di/src/DI/Config/Helpers.php',
|
||||
'Nette\\DI\\Config\\IAdapter' => __DIR__ . '/..' . '/nette/di/src/DI/Config/IAdapter.php',
|
||||
'Nette\\DI\\Config\\Loader' => __DIR__ . '/..' . '/nette/di/src/DI/Config/Loader.php',
|
||||
'Nette\\DI\\Container' => __DIR__ . '/..' . '/nette/di/src/DI/Container.php',
|
||||
'Nette\\DI\\ContainerBuilder' => __DIR__ . '/..' . '/nette/di/src/DI/ContainerBuilder.php',
|
||||
'Nette\\DI\\ContainerLoader' => __DIR__ . '/..' . '/nette/di/src/DI/ContainerLoader.php',
|
||||
'Nette\\DI\\DependencyChecker' => __DIR__ . '/..' . '/nette/di/src/DI/DependencyChecker.php',
|
||||
'Nette\\DI\\Extensions\\ConstantsExtension' => __DIR__ . '/..' . '/nette/di/src/DI/Extensions/ConstantsExtension.php',
|
||||
'Nette\\DI\\Extensions\\DIExtension' => __DIR__ . '/..' . '/nette/di/src/DI/Extensions/DIExtension.php',
|
||||
'Nette\\DI\\Extensions\\DecoratorExtension' => __DIR__ . '/..' . '/nette/di/src/DI/Extensions/DecoratorExtension.php',
|
||||
'Nette\\DI\\Extensions\\ExtensionsExtension' => __DIR__ . '/..' . '/nette/di/src/DI/Extensions/ExtensionsExtension.php',
|
||||
'Nette\\DI\\Extensions\\InjectExtension' => __DIR__ . '/..' . '/nette/di/src/DI/Extensions/InjectExtension.php',
|
||||
'Nette\\DI\\Extensions\\PhpExtension' => __DIR__ . '/..' . '/nette/di/src/DI/Extensions/PhpExtension.php',
|
||||
'Nette\\DI\\Helpers' => __DIR__ . '/..' . '/nette/di/src/DI/Helpers.php',
|
||||
'Nette\\DI\\MissingServiceException' => __DIR__ . '/..' . '/nette/di/src/DI/exceptions.php',
|
||||
'Nette\\DI\\PhpGenerator' => __DIR__ . '/..' . '/nette/di/src/DI/PhpGenerator.php',
|
||||
'Nette\\DI\\PhpReflection' => __DIR__ . '/..' . '/nette/di/src/DI/PhpReflection.php',
|
||||
'Nette\\DI\\ServiceCreationException' => __DIR__ . '/..' . '/nette/di/src/DI/exceptions.php',
|
||||
'Nette\\DI\\ServiceDefinition' => __DIR__ . '/..' . '/nette/di/src/DI/ServiceDefinition.php',
|
||||
'Nette\\DI\\Statement' => __DIR__ . '/..' . '/nette/di/src/DI/Statement.php',
|
||||
'Nette\\Database\\Connection' => __DIR__ . '/..' . '/nette/database/src/Database/Connection.php',
|
||||
'Nette\\Database\\ConnectionException' => __DIR__ . '/..' . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\ConstraintViolationException' => __DIR__ . '/..' . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\Context' => __DIR__ . '/..' . '/nette/database/src/Database/Context.php',
|
||||
'Nette\\Database\\Conventions\\AmbiguousReferenceKeyException' => __DIR__ . '/..' . '/nette/database/src/Database/Conventions/AmbiguousReferenceKeyException.php',
|
||||
'Nette\\Database\\Conventions\\DiscoveredConventions' => __DIR__ . '/..' . '/nette/database/src/Database/Conventions/DiscoveredConventions.php',
|
||||
'Nette\\Database\\Conventions\\StaticConventions' => __DIR__ . '/..' . '/nette/database/src/Database/Conventions/StaticConventions.php',
|
||||
'Nette\\Database\\DriverException' => __DIR__ . '/..' . '/nette/database/src/Database/DriverException.php',
|
||||
'Nette\\Database\\Drivers\\MsSqlDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/MsSqlDriver.php',
|
||||
'Nette\\Database\\Drivers\\MySqlDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/MySqlDriver.php',
|
||||
'Nette\\Database\\Drivers\\OciDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/OciDriver.php',
|
||||
'Nette\\Database\\Drivers\\OdbcDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/OdbcDriver.php',
|
||||
'Nette\\Database\\Drivers\\PgSqlDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/PgSqlDriver.php',
|
||||
'Nette\\Database\\Drivers\\Sqlite2Driver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/Sqlite2Driver.php',
|
||||
'Nette\\Database\\Drivers\\SqliteDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/SqliteDriver.php',
|
||||
'Nette\\Database\\Drivers\\SqlsrvDriver' => __DIR__ . '/..' . '/nette/database/src/Database/Drivers/SqlsrvDriver.php',
|
||||
'Nette\\Database\\ForeignKeyConstraintViolationException' => __DIR__ . '/..' . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\Helpers' => __DIR__ . '/..' . '/nette/database/src/Database/Helpers.php',
|
||||
'Nette\\Database\\IConventions' => __DIR__ . '/..' . '/nette/database/src/Database/IConventions.php',
|
||||
'Nette\\Database\\IRow' => __DIR__ . '/..' . '/nette/database/src/Database/IRow.php',
|
||||
'Nette\\Database\\IRowContainer' => __DIR__ . '/..' . '/nette/database/src/Database/IRowContainer.php',
|
||||
'Nette\\Database\\IStructure' => __DIR__ . '/..' . '/nette/database/src/Database/IStructure.php',
|
||||
'Nette\\Database\\ISupplementalDriver' => __DIR__ . '/..' . '/nette/database/src/Database/ISupplementalDriver.php',
|
||||
'Nette\\Database\\NotNullConstraintViolationException' => __DIR__ . '/..' . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\Database\\ResultSet' => __DIR__ . '/..' . '/nette/database/src/Database/ResultSet.php',
|
||||
'Nette\\Database\\Row' => __DIR__ . '/..' . '/nette/database/src/Database/Row.php',
|
||||
'Nette\\Database\\SqlLiteral' => __DIR__ . '/..' . '/nette/database/src/Database/SqlLiteral.php',
|
||||
'Nette\\Database\\SqlPreprocessor' => __DIR__ . '/..' . '/nette/database/src/Database/SqlPreprocessor.php',
|
||||
'Nette\\Database\\Structure' => __DIR__ . '/..' . '/nette/database/src/Database/Structure.php',
|
||||
'Nette\\Database\\Table\\ActiveRow' => __DIR__ . '/..' . '/nette/database/src/Database/Table/ActiveRow.php',
|
||||
'Nette\\Database\\Table\\GroupedSelection' => __DIR__ . '/..' . '/nette/database/src/Database/Table/GroupedSelection.php',
|
||||
'Nette\\Database\\Table\\IRow' => __DIR__ . '/..' . '/nette/database/src/Database/Table/IRow.php',
|
||||
'Nette\\Database\\Table\\IRowContainer' => __DIR__ . '/..' . '/nette/database/src/Database/Table/IRowContainer.php',
|
||||
'Nette\\Database\\Table\\Selection' => __DIR__ . '/..' . '/nette/database/src/Database/Table/Selection.php',
|
||||
'Nette\\Database\\Table\\SqlBuilder' => __DIR__ . '/..' . '/nette/database/src/Database/Table/SqlBuilder.php',
|
||||
'Nette\\Database\\UniqueConstraintViolationException' => __DIR__ . '/..' . '/nette/database/src/Database/exceptions.php',
|
||||
'Nette\\DeprecatedException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\DirectoryNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\FileNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Forms\\Container' => __DIR__ . '/..' . '/nette/forms/src/Forms/Container.php',
|
||||
'Nette\\Forms\\ControlGroup' => __DIR__ . '/..' . '/nette/forms/src/Forms/ControlGroup.php',
|
||||
'Nette\\Forms\\Controls\\BaseControl' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/BaseControl.php',
|
||||
'Nette\\Forms\\Controls\\Button' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/Button.php',
|
||||
'Nette\\Forms\\Controls\\Checkbox' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/Checkbox.php',
|
||||
'Nette\\Forms\\Controls\\CheckboxList' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/CheckboxList.php',
|
||||
'Nette\\Forms\\Controls\\ChoiceControl' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/ChoiceControl.php',
|
||||
'Nette\\Forms\\Controls\\CsrfProtection' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/CsrfProtection.php',
|
||||
'Nette\\Forms\\Controls\\HiddenField' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/HiddenField.php',
|
||||
'Nette\\Forms\\Controls\\ImageButton' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/ImageButton.php',
|
||||
'Nette\\Forms\\Controls\\MultiChoiceControl' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/MultiChoiceControl.php',
|
||||
'Nette\\Forms\\Controls\\MultiSelectBox' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/MultiSelectBox.php',
|
||||
'Nette\\Forms\\Controls\\RadioList' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/RadioList.php',
|
||||
'Nette\\Forms\\Controls\\SelectBox' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/SelectBox.php',
|
||||
'Nette\\Forms\\Controls\\SubmitButton' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/SubmitButton.php',
|
||||
'Nette\\Forms\\Controls\\TextArea' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/TextArea.php',
|
||||
'Nette\\Forms\\Controls\\TextBase' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/TextBase.php',
|
||||
'Nette\\Forms\\Controls\\TextInput' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/TextInput.php',
|
||||
'Nette\\Forms\\Controls\\UploadControl' => __DIR__ . '/..' . '/nette/forms/src/Forms/Controls/UploadControl.php',
|
||||
'Nette\\Forms\\Form' => __DIR__ . '/..' . '/nette/forms/src/Forms/Form.php',
|
||||
'Nette\\Forms\\Helpers' => __DIR__ . '/..' . '/nette/forms/src/Forms/Helpers.php',
|
||||
'Nette\\Forms\\IControl' => __DIR__ . '/..' . '/nette/forms/src/Forms/IControl.php',
|
||||
'Nette\\Forms\\IFormRenderer' => __DIR__ . '/..' . '/nette/forms/src/Forms/IFormRenderer.php',
|
||||
'Nette\\Forms\\ISubmitterControl' => __DIR__ . '/..' . '/nette/forms/src/Forms/ISubmitterControl.php',
|
||||
'Nette\\Forms\\Rendering\\DefaultFormRenderer' => __DIR__ . '/..' . '/nette/forms/src/Forms/Rendering/DefaultFormRenderer.php',
|
||||
'Nette\\Forms\\Rule' => __DIR__ . '/..' . '/nette/forms/src/Forms/Rule.php',
|
||||
'Nette\\Forms\\Rules' => __DIR__ . '/..' . '/nette/forms/src/Forms/Rules.php',
|
||||
'Nette\\Forms\\Validator' => __DIR__ . '/..' . '/nette/forms/src/Forms/Validator.php',
|
||||
'Nette\\Http\\Context' => __DIR__ . '/..' . '/nette/http/src/Http/Context.php',
|
||||
'Nette\\Http\\FileUpload' => __DIR__ . '/..' . '/nette/http/src/Http/FileUpload.php',
|
||||
'Nette\\Http\\Helpers' => __DIR__ . '/..' . '/nette/http/src/Http/Helpers.php',
|
||||
'Nette\\Http\\IRequest' => __DIR__ . '/..' . '/nette/http/src/Http/IRequest.php',
|
||||
'Nette\\Http\\IResponse' => __DIR__ . '/..' . '/nette/http/src/Http/IResponse.php',
|
||||
'Nette\\Http\\ISessionStorage' => __DIR__ . '/..' . '/nette/http/src/Http/ISessionStorage.php',
|
||||
'Nette\\Http\\Request' => __DIR__ . '/..' . '/nette/http/src/Http/Request.php',
|
||||
'Nette\\Http\\RequestFactory' => __DIR__ . '/..' . '/nette/http/src/Http/RequestFactory.php',
|
||||
'Nette\\Http\\Response' => __DIR__ . '/..' . '/nette/http/src/Http/Response.php',
|
||||
'Nette\\Http\\Session' => __DIR__ . '/..' . '/nette/http/src/Http/Session.php',
|
||||
'Nette\\Http\\SessionSection' => __DIR__ . '/..' . '/nette/http/src/Http/SessionSection.php',
|
||||
'Nette\\Http\\Url' => __DIR__ . '/..' . '/nette/http/src/Http/Url.php',
|
||||
'Nette\\Http\\UrlScript' => __DIR__ . '/..' . '/nette/http/src/Http/UrlScript.php',
|
||||
'Nette\\Http\\UserStorage' => __DIR__ . '/..' . '/nette/http/src/Http/UserStorage.php',
|
||||
'Nette\\IOException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\InvalidArgumentException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\InvalidStateException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Iterators\\CachingIterator' => __DIR__ . '/..' . '/nette/utils/src/Iterators/CachingIterator.php',
|
||||
'Nette\\Iterators\\Mapper' => __DIR__ . '/..' . '/nette/utils/src/Iterators/Mapper.php',
|
||||
'Nette\\LegacyObject' => __DIR__ . '/..' . '/nette/utils/src/Utils/LegacyObject.php',
|
||||
'Nette\\Loaders\\RobotLoader' => __DIR__ . '/..' . '/nette/robot-loader/src/RobotLoader/RobotLoader.php',
|
||||
'Nette\\Localization\\ITranslator' => __DIR__ . '/..' . '/nette/utils/src/Utils/ITranslator.php',
|
||||
'Nette\\Mail\\FallbackMailer' => __DIR__ . '/..' . '/nette/mail/src/Mail/FallbackMailer.php',
|
||||
'Nette\\Mail\\FallbackMailerException' => __DIR__ . '/..' . '/nette/mail/src/Mail/exceptions.php',
|
||||
'Nette\\Mail\\IMailer' => __DIR__ . '/..' . '/nette/mail/src/Mail/IMailer.php',
|
||||
'Nette\\Mail\\Message' => __DIR__ . '/..' . '/nette/mail/src/Mail/Message.php',
|
||||
'Nette\\Mail\\MimePart' => __DIR__ . '/..' . '/nette/mail/src/Mail/MimePart.php',
|
||||
'Nette\\Mail\\SendException' => __DIR__ . '/..' . '/nette/mail/src/Mail/exceptions.php',
|
||||
'Nette\\Mail\\SendmailMailer' => __DIR__ . '/..' . '/nette/mail/src/Mail/SendmailMailer.php',
|
||||
'Nette\\Mail\\SmtpException' => __DIR__ . '/..' . '/nette/mail/src/Mail/exceptions.php',
|
||||
'Nette\\Mail\\SmtpMailer' => __DIR__ . '/..' . '/nette/mail/src/Mail/SmtpMailer.php',
|
||||
'Nette\\MemberAccessException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Neon\\Decoder' => __DIR__ . '/..' . '/nette/neon/src/Neon/Decoder.php',
|
||||
'Nette\\Neon\\Encoder' => __DIR__ . '/..' . '/nette/neon/src/Neon/Encoder.php',
|
||||
'Nette\\Neon\\Entity' => __DIR__ . '/..' . '/nette/neon/src/Neon/Entity.php',
|
||||
'Nette\\Neon\\Exception' => __DIR__ . '/..' . '/nette/neon/src/Neon/Exception.php',
|
||||
'Nette\\Neon\\Neon' => __DIR__ . '/..' . '/nette/neon/src/Neon/Neon.php',
|
||||
'Nette\\NotImplementedException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\NotSupportedException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\OutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\PhpGenerator\\ClassType' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/ClassType.php',
|
||||
'Nette\\PhpGenerator\\Closure' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Closure.php',
|
||||
'Nette\\PhpGenerator\\Constant' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Constant.php',
|
||||
'Nette\\PhpGenerator\\Factory' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Factory.php',
|
||||
'Nette\\PhpGenerator\\GlobalFunction' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/GlobalFunction.php',
|
||||
'Nette\\PhpGenerator\\Helpers' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Helpers.php',
|
||||
'Nette\\PhpGenerator\\Method' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Method.php',
|
||||
'Nette\\PhpGenerator\\Parameter' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Parameter.php',
|
||||
'Nette\\PhpGenerator\\PhpFile' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/PhpFile.php',
|
||||
'Nette\\PhpGenerator\\PhpLiteral' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/PhpLiteral.php',
|
||||
'Nette\\PhpGenerator\\PhpNamespace' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/PhpNamespace.php',
|
||||
'Nette\\PhpGenerator\\Printer' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Printer.php',
|
||||
'Nette\\PhpGenerator\\Property' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Property.php',
|
||||
'Nette\\PhpGenerator\\PsrPrinter' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/PsrPrinter.php',
|
||||
'Nette\\PhpGenerator\\Traits\\CommentAware' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Traits/CommentAware.php',
|
||||
'Nette\\PhpGenerator\\Traits\\FunctionLike' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Traits/FunctionLike.php',
|
||||
'Nette\\PhpGenerator\\Traits\\NameAware' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Traits/NameAware.php',
|
||||
'Nette\\PhpGenerator\\Traits\\VisibilityAware' => __DIR__ . '/..' . '/nette/php-generator/src/PhpGenerator/Traits/VisibilityAware.php',
|
||||
'Nette\\Reflection\\Annotation' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/Annotation.php',
|
||||
'Nette\\Reflection\\AnnotationsParser' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/AnnotationsParser.php',
|
||||
'Nette\\Reflection\\ClassType' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/ClassType.php',
|
||||
'Nette\\Reflection\\Extension' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/Extension.php',
|
||||
'Nette\\Reflection\\GlobalFunction' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/GlobalFunction.php',
|
||||
'Nette\\Reflection\\Helpers' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/Helpers.php',
|
||||
'Nette\\Reflection\\IAnnotation' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/IAnnotation.php',
|
||||
'Nette\\Reflection\\Method' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/Method.php',
|
||||
'Nette\\Reflection\\Parameter' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/Parameter.php',
|
||||
'Nette\\Reflection\\Property' => __DIR__ . '/..' . '/nette/reflection/src/Reflection/Property.php',
|
||||
'Nette\\Security\\AuthenticationException' => __DIR__ . '/..' . '/nette/security/src/Security/AuthenticationException.php',
|
||||
'Nette\\Security\\IAuthenticator' => __DIR__ . '/..' . '/nette/security/src/Security/IAuthenticator.php',
|
||||
'Nette\\Security\\IAuthorizator' => __DIR__ . '/..' . '/nette/security/src/Security/IAuthorizator.php',
|
||||
'Nette\\Security\\IIdentity' => __DIR__ . '/..' . '/nette/security/src/Security/IIdentity.php',
|
||||
'Nette\\Security\\IResource' => __DIR__ . '/..' . '/nette/security/src/Security/IResource.php',
|
||||
'Nette\\Security\\IRole' => __DIR__ . '/..' . '/nette/security/src/Security/IRole.php',
|
||||
'Nette\\Security\\IUserStorage' => __DIR__ . '/..' . '/nette/security/src/Security/IUserStorage.php',
|
||||
'Nette\\Security\\Identity' => __DIR__ . '/..' . '/nette/security/src/Security/Identity.php',
|
||||
'Nette\\Security\\Passwords' => __DIR__ . '/..' . '/nette/security/src/Security/Passwords.php',
|
||||
'Nette\\Security\\Permission' => __DIR__ . '/..' . '/nette/security/src/Security/Permission.php',
|
||||
'Nette\\Security\\SimpleAuthenticator' => __DIR__ . '/..' . '/nette/security/src/Security/SimpleAuthenticator.php',
|
||||
'Nette\\Security\\User' => __DIR__ . '/..' . '/nette/security/src/Security/User.php',
|
||||
'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/Utils/SmartObject.php',
|
||||
'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/Utils/StaticClass.php',
|
||||
'Nette\\StaticClassException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ArrayHash' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayHash.php',
|
||||
'Nette\\Utils\\ArrayList' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayList.php',
|
||||
'Nette\\Utils\\Arrays' => __DIR__ . '/..' . '/nette/utils/src/Utils/Arrays.php',
|
||||
'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php',
|
||||
'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php',
|
||||
'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php',
|
||||
'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/finder/src/Utils/Finder.php',
|
||||
'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php',
|
||||
'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/Utils/IHtmlString.php',
|
||||
'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php',
|
||||
'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php',
|
||||
'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php',
|
||||
'Nette\\Utils\\ObjectMixin' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectMixin.php',
|
||||
'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php',
|
||||
'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php',
|
||||
'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php',
|
||||
'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php',
|
||||
'Nette\\Utils\\UnknownImageFileException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Validators' => __DIR__ . '/..' . '/nette/utils/src/Utils/Validators.php',
|
||||
'Tester\\Assert' => __DIR__ . '/..' . '/nette/tester/src/Framework/Assert.php',
|
||||
'Tester\\AssertException' => __DIR__ . '/..' . '/nette/tester/src/Framework/AssertException.php',
|
||||
'Tester\\CodeCoverage\\Collector' => __DIR__ . '/..' . '/nette/tester/src/CodeCoverage/Collector.php',
|
||||
'Tester\\CodeCoverage\\Generators\\AbstractGenerator' => __DIR__ . '/..' . '/nette/tester/src/CodeCoverage/Generators/AbstractGenerator.php',
|
||||
'Tester\\CodeCoverage\\Generators\\CloverXMLGenerator' => __DIR__ . '/..' . '/nette/tester/src/CodeCoverage/Generators/CloverXMLGenerator.php',
|
||||
'Tester\\CodeCoverage\\Generators\\HtmlGenerator' => __DIR__ . '/..' . '/nette/tester/src/CodeCoverage/Generators/HtmlGenerator.php',
|
||||
'Tester\\CodeCoverage\\PhpParser' => __DIR__ . '/..' . '/nette/tester/src/CodeCoverage/PhpParser.php',
|
||||
'Tester\\DataProvider' => __DIR__ . '/..' . '/nette/tester/src/Framework/DataProvider.php',
|
||||
'Tester\\DomQuery' => __DIR__ . '/..' . '/nette/tester/src/Framework/DomQuery.php',
|
||||
'Tester\\Dumper' => __DIR__ . '/..' . '/nette/tester/src/Framework/Dumper.php',
|
||||
'Tester\\Environment' => __DIR__ . '/..' . '/nette/tester/src/Framework/Environment.php',
|
||||
'Tester\\Expect' => __DIR__ . '/..' . '/nette/tester/src/Framework/Expect.php',
|
||||
'Tester\\FileMock' => __DIR__ . '/..' . '/nette/tester/src/Framework/FileMock.php',
|
||||
'Tester\\FileMutator' => __DIR__ . '/..' . '/nette/tester/src/Framework/FileMutator.php',
|
||||
'Tester\\Helpers' => __DIR__ . '/..' . '/nette/tester/src/Framework/Helpers.php',
|
||||
'Tester\\Runner\\CliTester' => __DIR__ . '/..' . '/nette/tester/src/Runner/CliTester.php',
|
||||
'Tester\\Runner\\CommandLine' => __DIR__ . '/..' . '/nette/tester/src/Runner/CommandLine.php',
|
||||
'Tester\\Runner\\Job' => __DIR__ . '/..' . '/nette/tester/src/Runner/Job.php',
|
||||
'Tester\\Runner\\OutputHandler' => __DIR__ . '/..' . '/nette/tester/src/Runner/OutputHandler.php',
|
||||
'Tester\\Runner\\Output\\ConsolePrinter' => __DIR__ . '/..' . '/nette/tester/src/Runner/Output/ConsolePrinter.php',
|
||||
'Tester\\Runner\\Output\\JUnitPrinter' => __DIR__ . '/..' . '/nette/tester/src/Runner/Output/JUnitPrinter.php',
|
||||
'Tester\\Runner\\Output\\Logger' => __DIR__ . '/..' . '/nette/tester/src/Runner/Output/Logger.php',
|
||||
'Tester\\Runner\\Output\\TapPrinter' => __DIR__ . '/..' . '/nette/tester/src/Runner/Output/TapPrinter.php',
|
||||
'Tester\\Runner\\PhpInterpreter' => __DIR__ . '/..' . '/nette/tester/src/Runner/PhpInterpreter.php',
|
||||
'Tester\\Runner\\Runner' => __DIR__ . '/..' . '/nette/tester/src/Runner/Runner.php',
|
||||
'Tester\\Runner\\Test' => __DIR__ . '/..' . '/nette/tester/src/Runner/Test.php',
|
||||
'Tester\\Runner\\TestHandler' => __DIR__ . '/..' . '/nette/tester/src/Runner/TestHandler.php',
|
||||
'Tester\\TestCase' => __DIR__ . '/..' . '/nette/tester/src/Framework/TestCase.php',
|
||||
'Tester\\TestCaseException' => __DIR__ . '/..' . '/nette/tester/src/Framework/TestCase.php',
|
||||
'Tracy\\Bar' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Bar/Bar.php',
|
||||
'Tracy\\BlueScreen' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/BlueScreen/BlueScreen.php',
|
||||
'Tracy\\Bridges\\Nette\\Bridge' => __DIR__ . '/..' . '/tracy/tracy/src/Bridges/Nette/Bridge.php',
|
||||
'Tracy\\Bridges\\Nette\\MailSender' => __DIR__ . '/..' . '/tracy/tracy/src/Bridges/Nette/MailSender.php',
|
||||
'Tracy\\Bridges\\Nette\\TracyExtension' => __DIR__ . '/..' . '/tracy/tracy/src/Bridges/Nette/TracyExtension.php',
|
||||
'Tracy\\Bridges\\Psr\\PsrToTracyLoggerAdapter' => __DIR__ . '/..' . '/tracy/tracy/src/Bridges/Psr/PsrToTracyLoggerAdapter.php',
|
||||
'Tracy\\Bridges\\Psr\\TracyToPsrLoggerAdapter' => __DIR__ . '/..' . '/tracy/tracy/src/Bridges/Psr/TracyToPsrLoggerAdapter.php',
|
||||
'Tracy\\Debugger' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Debugger/Debugger.php',
|
||||
'Tracy\\DefaultBarPanel' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Bar/DefaultBarPanel.php',
|
||||
'Tracy\\Dumper' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Dumper/Dumper.php',
|
||||
'Tracy\\FireLogger' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Logger/FireLogger.php',
|
||||
'Tracy\\Helpers' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Helpers.php',
|
||||
'Tracy\\IBarPanel' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Bar/IBarPanel.php',
|
||||
'Tracy\\ILogger' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Logger/ILogger.php',
|
||||
'Tracy\\Logger' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/Logger/Logger.php',
|
||||
'Tracy\\OutputDebugger' => __DIR__ . '/..' . '/tracy/tracy/src/Tracy/OutputDebugger/OutputDebugger.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->classMap = ComposerStaticInit1e4e7be4b54097efddcd9fd472eda330::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Vendored
+1344
File diff suppressed because it is too large
Load Diff
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "latte/latte",
|
||||
"description": "☕ Latte: the intuitive and fast template engine for those who want the most secure PHP sites. Introduces context-sensitive escaping.",
|
||||
"keywords": ["template", "nette", "security", "engine", "html", "twig", "context-sensitive", "escaping"],
|
||||
"homepage": "https://latte.nette.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
"tracy/tracy": "^2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "to use filters like lower, upper, capitalize, ...",
|
||||
"ext-fileinfo": "to use filter |datastream",
|
||||
"nette/utils": "to use filter |webalize"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/application": "<2.4.1"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.5-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "latte/latte",
|
||||
"description": "☕ Latte: the intuitive and fast template engine for those who want the most secure PHP sites. Introduces context-sensitive escaping.",
|
||||
"keywords": ["template", "nette", "security", "engine", "html", "twig", "context-sensitive", "escaping"],
|
||||
"homepage": "https://latte.nette.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
"tracy/tracy": "^2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "to use filters like lower, upper, capitalize, ...",
|
||||
"ext-fileinfo": "to use filter |datastream",
|
||||
"nette/utils": "to use filter |webalize"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/application": "<2.4.1"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.5-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Framework under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
The BSD License is recommended for most projects. It is easy to understand and it
|
||||
places almost no restrictions on what you can do with the framework. If the GPL
|
||||
fits better to your project, you can use the framework under this license.
|
||||
|
||||
You don't have to notify anyone which license you are using. You can freely
|
||||
use Nette Framework in commercial projects as long as the copyright header
|
||||
remains intact.
|
||||
|
||||
Please be advised that the name "Nette Framework" is a protected trademark and its
|
||||
usage has some limitations. So please do not use word "Nette" in the name of your
|
||||
project or top-level domain, and choose a name that stands on its own merits.
|
||||
If your stuff is good, it will not take long to establish a reputation for yourselves.
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Framework" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
Vendored
+314
@@ -0,0 +1,314 @@
|
||||
[Latte](https://latte.nette.org): amazing template engine for PHP
|
||||
=================================================================
|
||||
|
||||
[](https://packagist.org/packages/latte/latte)
|
||||
[](https://travis-ci.org/nette/latte)
|
||||
[](https://coveralls.io/github/nette/latte?branch=master)
|
||||
[](https://github.com/nette/latte/releases)
|
||||
[](https://github.com/nette/latte/blob/master/license.md)
|
||||
[](https://gitter.im/nette/latte)
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Latte is a template engine for PHP which eases your work and
|
||||
ensures the output is protected against vulnerabilities, such as XSS.
|
||||
|
||||
- **Latte is fast:** it compiles templates to plain optimized PHP code.
|
||||
- **Latte is secure:** it is the first PHP engine introducing content-aware escaping.
|
||||
- **Latte speaks your language:** it has intuitive syntax and helps you to build better websites easily.
|
||||
|
||||
Documentation can be found on the [website](https://latte.nette.org).
|
||||
|
||||
If you like Latte, **[please make a donation now](https://nette.org/make-donation?to=latte)**. Thank you!
|
||||
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
Although PHP is originally a templating language, it is not particularly suited for writing templates. Let's have a look at an example of a PHP template that prints an array `$items` as a list:
|
||||
|
||||
```php
|
||||
<?php if ($items): ?>
|
||||
<?php $counter = 1 ?>
|
||||
<ul>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<li id="item-<?php echo $counter++ ?>"><?php
|
||||
echo htmlSpecialChars(mb_convert_case($item, MB_CASE_TITLE)) ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php endif?>
|
||||
```
|
||||
|
||||
The code is rather confusing. Moreover, we must not forget to call `htmlSpecialChars` function. That's why there are so many different template engines for PHP. One of the best template engines is part of Nette Framework and it is called **Latte**. You'll love it!
|
||||
|
||||
The same template as the one above can be written easily in Latte:
|
||||
|
||||
```html
|
||||
<ul n:if="$items">
|
||||
{foreach $items as $item}
|
||||
<li id="item-{$iterator->counter}">{$item|capitalize}</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
```
|
||||
|
||||
As you can see there are two types of macros:
|
||||
|
||||
- **macro** in braces, for example `{foreach …}`
|
||||
- **n:macro**, for example `n:if="…"`
|
||||
|
||||
How to render template? Just install Latte (see below) and run this code:
|
||||
|
||||
```php
|
||||
$latte = new Latte\Engine;
|
||||
$latte->setTempDirectory('/path/to/tempdir');
|
||||
$parameters['items'] = array('one', 'two', 'three');
|
||||
$latte->render('template.latte', $parameters);
|
||||
```
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
The recommended way to install Latte is via Composer (alternatively you can [download package](https://github.com/nette/latte/releases)):
|
||||
|
||||
```
|
||||
composer require latte/latte
|
||||
```
|
||||
|
||||
Latte requires PHP version 5.4.4 and supports PHP up to 7.3. The dev-master version requires PHP 7.1.
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
You can find detailed description of all the default macros on the [extra page](https://doc.nette.org/en/default-macros). Furthermore, you can make your own macros.
|
||||
|
||||
Each pair macro, such as `{if} … {/if}`, operating upon single HTML element can be written in `n:macro` notation. So, it is possible to write the `{foreach}` macro in the same manner:
|
||||
|
||||
```html
|
||||
<ul n:if="$items">
|
||||
<li n:foreach="$items as $item">{$item|capitalize}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
With n:macros you can do much more interesting tricks as you will see in a moment.
|
||||
|
||||
`{$item|capitalize}` macro which prints the `$item` variable contains so called filter, in this case the `capitalize` filter which makes the first letter of each word uppercase.
|
||||
|
||||
Very important feature of Latte is that it **escapes variables by default**. Escaping is needed when printing a variable because we have to convert all the characters which have a special meaning in HTML to other sequences. In case we forget it can lead to a serious security hole called Cross Site Scripting (XSS).
|
||||
|
||||
Because of different escaping functions that are needed in different documents and different parts of a page, Latte features a unique technology of Context-Aware Escaping which recognizes the context in which the macro is placed and **chooses the right escaping mode.** You don't have to worry that your coder forgets about it causing you goose bumps because of a security hole. Which is great!
|
||||
|
||||
If the `$item` variable stores an HTML code and you want to print it without any alteration you just add the modifier noescape: `{$item|noescape}`. Forgetting the modifier mark won't cause any security holes in spirit of „less code, more security“ principle.
|
||||
|
||||
You can still use PHP inside the macros normally, including comments as well. But Latte also extends the PHP syntax with three pleasant features:
|
||||
|
||||
1. array can be written as `[1, 2, 3]`, which is the same as `array(1, 2, 3)` in PHP
|
||||
3. we can omit quotes around the strings consisting of letters, numbers and dashes
|
||||
2. short condition notation `$a ? 'b'` which is the same as `$a ? 'b' : null` in PHP
|
||||
|
||||
For example:
|
||||
|
||||
```html
|
||||
{foreach [a, b, c] as $id} ... {/foreach}
|
||||
|
||||
{$cond ? hello} // prints 'hello' if $cond equals true
|
||||
```
|
||||
|
||||
Latte also has a `{* comment macro *}` which doesn't get printed to the output.
|
||||
|
||||
|
||||
n:macros
|
||||
========
|
||||
|
||||
We showed that n:macros are supposed to be written directly into HTML tags as their special attributes. We also said that every pair macro (e.g. `{if} … {/if}`) can be written in n:macro notation. The macro then corresponds to the HTML element in which it is written:
|
||||
|
||||
```html
|
||||
{var $items = ['I', '♥', 'Nette Framework']}
|
||||
|
||||
<p n:foreach="$items as $item">{$item}</p>
|
||||
```
|
||||
|
||||
Prints:
|
||||
|
||||
```html
|
||||
<p>I</p>
|
||||
<p>♥</p>
|
||||
<p>Nette Framework</p>
|
||||
```
|
||||
|
||||
By using `inner-` prefix we can alter the behavior so that the macro applies only to the body of the element:
|
||||
|
||||
```html
|
||||
<div n:inner-foreach="$items as $item">
|
||||
<p>{$item}</p>
|
||||
<hr>
|
||||
</div>
|
||||
```
|
||||
|
||||
Prints:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<p>I</p>
|
||||
<hr>
|
||||
<p>♥</p>
|
||||
<hr>
|
||||
<p>Nette Framework</p>
|
||||
<hr>
|
||||
</div>
|
||||
```
|
||||
|
||||
Or by using `tag-` prefix the macro is applied on the HTML tags only:
|
||||
|
||||
```html
|
||||
<p><a href="{$url}" n:tag-if="$url">Title</a></p>
|
||||
```
|
||||
|
||||
Depending on the value of `$url` variable this will print:
|
||||
|
||||
```html
|
||||
// when $url is empty
|
||||
<p>Title</p>
|
||||
|
||||
// when $url equals 'https://nette.org'
|
||||
<p><a href="https://nette.org">Title</a></p>
|
||||
```
|
||||
|
||||
However, n:macros are not only a shortcut for pair macros, there are some pure n:macros as well, for example the coder's best friend [n:class](https://doc.nette.org/en/default-macros#toc-n-class) macro.
|
||||
|
||||
|
||||
Filters
|
||||
=======
|
||||
|
||||
Latte allows calling filters by using the pipe sign notation (preceding space is allowed):
|
||||
|
||||
```html
|
||||
<h1>{$heading|upper}</h1>
|
||||
```
|
||||
|
||||
Filters (or modifiers) can be chained, in that case they apply in order from left to right:
|
||||
|
||||
```html
|
||||
<h1>{$heading|lower|capitalize}</h1>
|
||||
```
|
||||
|
||||
Parameters are put after the filter name separated by colon or comma:
|
||||
|
||||
```html
|
||||
<h1>{$heading|truncate:20,''}</h1>
|
||||
```
|
||||
|
||||
See the summary of [standard filters](https://doc.nette.org/en/default-filters) and how to make user-defined filters.
|
||||
|
||||
|
||||
In templates we can use functions which change or format the data to a form we want. They are called *filters*. See the summary of the default filters.
|
||||
|
||||
|
||||
Filter can be registered by any callback or lambda function:
|
||||
|
||||
```php
|
||||
$latte = new Latte\Engine;
|
||||
$latte->addFilter('shortify', function ($s) {
|
||||
return mb_substr($s, 0, 10); // shortens the text to 10 characters
|
||||
});
|
||||
```
|
||||
|
||||
In this case it would be better for the filter to get an extra parameter:
|
||||
|
||||
```php
|
||||
$latte->addFilter('shortify', function ($s, $len = 10) {
|
||||
return mb_substr($s, 0, $len);
|
||||
});
|
||||
```
|
||||
|
||||
We call it in a template like this:
|
||||
|
||||
```php
|
||||
<p><?php echo $template->shortify($text, 100); ?></p>
|
||||
```
|
||||
|
||||
Latte simplifies the notation - filters are denoted by the pipe sign, they can be chained (they apply in order from left to right). Parameters are separated by colon or comma:
|
||||
|
||||
```html
|
||||
<p>{$text|shortify:100}</p>
|
||||
```
|
||||
|
||||
|
||||
Performance
|
||||
===========
|
||||
|
||||
Latte is fast. It compiles the templates to native PHP code and stores them in cache on the disk. So they are as fast as if they would have been written in pure PHP.
|
||||
|
||||
The template is automatically recompiled each time we change the source file. While developing you just need to edit the templates in Latte and changes are visible in your browser instantly.
|
||||
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
With each error or typo you will be informed by the Debugger with all the luxury. The template source code is displayed and the red line marks the error showing error message as well. With just a single click you can open the template in your favorite editor and fix the error at once. Easy peasy!
|
||||
|
||||
If you are using an IDE with code stepping you can go through the generated PHP code of the template.
|
||||
|
||||
|
||||
|
||||
Usability
|
||||
=========
|
||||
|
||||
Latte syntax wasn't invented by engineers but came up from webdesigner's practical requests. We were looking for the friendliest syntax with which you can write even the most problematic constructions comfortably enough. You will be surprised how much help Latte can be.
|
||||
|
||||
You can find macros for advanced [layout](https://doc.nette.org/en/default-macros#toc-template-expanding-inheritance) managing, for **template inheritance**, nested blocks and so on. Syntax comes from PHP itself so you don't have to learn anything new and you can leverage your know-how.
|
||||
|
||||
|
||||
|
||||
Context-Aware Escaping
|
||||
======================
|
||||
|
||||
Although the Cross Site Scripting (XSS) is one of the trivial ways of exploiting a web page it is the most common vulnerability but very serious. It can lead to identity theft and so on. The best defense is consistent escaping of printed data, ie. converting the characters which have a special meaning in the given context.
|
||||
|
||||
If the coder omits the escaping a security hole is made. That's why template engines implement automated escaping. The problem is that the web page has different contexts and each has different rules for escaping printed data. A security hole then shows up if the wrong escaping functions are used.
|
||||
|
||||
But Latte is sophisticated. It features unique technology of *Context-Aware Escaping* which recognizes the context in which the macro is placed and chooses the right escaping mode. What does that mean?
|
||||
|
||||
Latte doesn't need any manual work. **All is done automatically, consistently and correctly.** You don't have to worry about security holes.
|
||||
|
||||
Lets see how it works:
|
||||
|
||||
```html
|
||||
<p onclick="alert({$movie})">{$movie}</p>
|
||||
|
||||
<script>var movie = {$movie};</script>
|
||||
```
|
||||
|
||||
If `$movie` variable stores `'Amarcord & 8 1/2'` string it generates the following output. Notice different escaping used in HTML and JavaScript and also in `onclick` attribute:
|
||||
|
||||
|
||||
```html
|
||||
<p onclick="alert("Amarcord & 8 1\/2")">Amarcord & 8 1/2</p>
|
||||
|
||||
<script>var movie = "Amarcord & 8 1\/2";</script>
|
||||
```
|
||||
|
||||
Thanks to Context-Aware Escaping the template is simple and your application perfectly secured against Cross Site Scripting. You can use PHP variables natively inside the JavaScript!
|
||||
|
||||
|
||||
JavaScript
|
||||
----------
|
||||
|
||||
Strings in JavaScript are escaped including quotes. If you want to put variable into another string, simply concatenate them:
|
||||
|
||||
```html
|
||||
<script>
|
||||
alert('Hello ' + {$name} + '!'); # good
|
||||
alert('Hello {$name} !'); # bad
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
A pretty output
|
||||
===============
|
||||
|
||||
Sticklers will enjoy the look of the HTML output which Latte generates. All tags are indented as they are supposed to. The code looks like it has been processed with some kind of *HTML code beautifier* :-)
|
||||
+738
@@ -0,0 +1,738 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Latte compiler.
|
||||
*/
|
||||
class Compiler
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** Context-aware escaping content types */
|
||||
public const
|
||||
CONTENT_HTML = Engine::CONTENT_HTML,
|
||||
CONTENT_XHTML = Engine::CONTENT_XHTML,
|
||||
CONTENT_XML = Engine::CONTENT_XML,
|
||||
CONTENT_JS = Engine::CONTENT_JS,
|
||||
CONTENT_CSS = Engine::CONTENT_CSS,
|
||||
CONTENT_ICAL = Engine::CONTENT_ICAL,
|
||||
CONTENT_TEXT = Engine::CONTENT_TEXT;
|
||||
|
||||
/** @internal Context-aware escaping HTML contexts */
|
||||
public const
|
||||
CONTEXT_HTML_TEXT = null,
|
||||
CONTEXT_HTML_TAG = 'Tag',
|
||||
CONTEXT_HTML_ATTRIBUTE = 'Attr',
|
||||
CONTEXT_HTML_ATTRIBUTE_JS = 'AttrJs',
|
||||
CONTEXT_HTML_ATTRIBUTE_CSS = 'AttrCss',
|
||||
CONTEXT_HTML_ATTRIBUTE_URL = 'AttrUrl',
|
||||
CONTEXT_HTML_ATTRIBUTE_UNQUOTED_URL = 'AttrUnquotedUrl',
|
||||
CONTEXT_HTML_COMMENT = 'Comment',
|
||||
CONTEXT_HTML_BOGUS_COMMENT = 'Bogus',
|
||||
CONTEXT_HTML_CSS = 'Css',
|
||||
CONTEXT_HTML_JS = 'Js',
|
||||
|
||||
CONTEXT_XML_TEXT = self::CONTEXT_HTML_TEXT,
|
||||
CONTEXT_XML_TAG = self::CONTEXT_HTML_TAG,
|
||||
CONTEXT_XML_ATTRIBUTE = self::CONTEXT_HTML_ATTRIBUTE,
|
||||
CONTEXT_XML_COMMENT = self::CONTEXT_HTML_COMMENT,
|
||||
CONTEXT_XML_BOGUS_COMMENT = self::CONTEXT_HTML_BOGUS_COMMENT;
|
||||
|
||||
/** @var Token[] */
|
||||
private $tokens;
|
||||
|
||||
/** @var string pointer to current node content */
|
||||
private $output;
|
||||
|
||||
/** @var int position on source template */
|
||||
private $position;
|
||||
|
||||
/** @var array of [name => IMacro[]] */
|
||||
private $macros = [];
|
||||
|
||||
/** @var int[] IMacro flags */
|
||||
private $flags;
|
||||
|
||||
/** @var HtmlNode */
|
||||
private $htmlNode;
|
||||
|
||||
/** @var MacroNode */
|
||||
private $macroNode;
|
||||
|
||||
/** @var string[] */
|
||||
private $placeholders = [];
|
||||
|
||||
/** @var string */
|
||||
private $contentType = self::CONTENT_HTML;
|
||||
|
||||
/** @var string|null */
|
||||
private $context;
|
||||
|
||||
/** @var mixed */
|
||||
private $lastAttrValue;
|
||||
|
||||
/** @var int */
|
||||
private $tagOffset;
|
||||
|
||||
/** @var bool */
|
||||
private $inHead;
|
||||
|
||||
/** @var array of [name => [body, arguments]] */
|
||||
private $methods = [];
|
||||
|
||||
/** @var array of [name => serialized value] */
|
||||
private $properties = [];
|
||||
|
||||
|
||||
/**
|
||||
* Adds new macro with IMacro flags.
|
||||
* @return static
|
||||
*/
|
||||
public function addMacro(string $name, IMacro $macro, int $flags = null)
|
||||
{
|
||||
if (!isset($this->flags[$name])) {
|
||||
$this->flags[$name] = $flags ?: IMacro::DEFAULT_FLAGS;
|
||||
} elseif ($flags && $this->flags[$name] !== $flags) {
|
||||
throw new \LogicException("Incompatible flags for macro $name.");
|
||||
}
|
||||
$this->macros[$name][] = $macro;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compiles tokens to PHP code.
|
||||
* @param Token[] $tokens
|
||||
*/
|
||||
public function compile(array $tokens, string $className): string
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
$output = '';
|
||||
$this->output = &$output;
|
||||
$this->inHead = true;
|
||||
$this->htmlNode = $this->macroNode = $this->context = null;
|
||||
$this->placeholders = $this->properties = [];
|
||||
$this->methods = ['main' => null, 'prepare' => null];
|
||||
|
||||
$macroHandlers = new \SplObjectStorage;
|
||||
|
||||
if ($this->macros) {
|
||||
array_map([$macroHandlers, 'attach'], array_merge(...array_values($this->macros)));
|
||||
}
|
||||
|
||||
foreach ($macroHandlers as $handler) {
|
||||
$handler->initialize($this);
|
||||
}
|
||||
|
||||
foreach ($tokens as $this->position => $token) {
|
||||
if ($this->inHead && !($token->type === $token::COMMENT
|
||||
|| $token->type === $token::MACRO_TAG && ($this->flags[$token->name] ?? null) & IMacro::ALLOWED_IN_HEAD
|
||||
|| $token->type === $token::TEXT && trim($token->text) === ''
|
||||
)) {
|
||||
$this->inHead = false;
|
||||
}
|
||||
$this->{"process$token->type"}($token);
|
||||
}
|
||||
|
||||
while ($this->htmlNode) {
|
||||
if (!empty($this->htmlNode->macroAttrs)) {
|
||||
throw new CompileException('Missing ' . self::printEndTag($this->htmlNode));
|
||||
}
|
||||
$this->htmlNode = $this->htmlNode->parentNode;
|
||||
}
|
||||
|
||||
while ($this->macroNode) {
|
||||
if (~$this->flags[$this->macroNode->name] & IMacro::AUTO_CLOSE) {
|
||||
throw new CompileException('Missing ' . self::printEndTag($this->macroNode));
|
||||
}
|
||||
$this->closeMacro($this->macroNode->name);
|
||||
}
|
||||
|
||||
$prepare = $epilogs = '';
|
||||
foreach ($macroHandlers as $handler) {
|
||||
$res = $handler->finalize();
|
||||
$prepare .= empty($res[0]) ? '' : "<?php $res[0] ?>";
|
||||
$epilogs = (empty($res[1]) ? '' : "<?php $res[1] ?>") . $epilogs;
|
||||
}
|
||||
|
||||
$this->addMethod('main', $this->expandTokens("extract(\$this->params);?>\n$output$epilogs<?php return get_defined_vars();"));
|
||||
|
||||
if ($prepare) {
|
||||
$this->addMethod('prepare', "extract(\$this->params);?>$prepare<?php");
|
||||
}
|
||||
if ($this->contentType !== self::CONTENT_HTML) {
|
||||
$this->addProperty('contentType', $this->contentType);
|
||||
}
|
||||
|
||||
foreach ($this->properties as $name => $value) {
|
||||
$members[] = "\tpublic $$name = " . PhpHelpers::dump($value) . ';';
|
||||
}
|
||||
foreach (array_filter($this->methods) as $name => $method) {
|
||||
$members[] = "\n\tfunction $name($method[arguments])\n\t{\n" . ($method['body'] ? "\t\t$method[body]\n" : '') . "\t}";
|
||||
}
|
||||
|
||||
return "<?php\n"
|
||||
. "use Latte\\Runtime as LR;\n\n"
|
||||
. "class $className extends Latte\\Runtime\\Template\n{\n"
|
||||
. implode("\n\n", $members)
|
||||
. "\n\n}\n";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function setContentType(string $type)
|
||||
{
|
||||
$this->contentType = $type;
|
||||
$this->context = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getMacroNode(): ?MacroNode
|
||||
{
|
||||
return $this->macroNode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current line number.
|
||||
*/
|
||||
public function getLine(): ?int
|
||||
{
|
||||
return isset($this->tokens[$this->position]) ? $this->tokens[$this->position]->line : null;
|
||||
}
|
||||
|
||||
|
||||
public function isInHead(): bool
|
||||
{
|
||||
return $this->inHead;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds custom method to template.
|
||||
* @internal
|
||||
*/
|
||||
public function addMethod(string $name, string $body, string $arguments = ''): void
|
||||
{
|
||||
$this->methods[$name] = ['body' => trim($body), 'arguments' => $arguments];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns custom methods.
|
||||
* @internal
|
||||
*/
|
||||
public function getMethods(): array
|
||||
{
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds custom property to template.
|
||||
* @internal
|
||||
*/
|
||||
public function addProperty(string $name, $value): void
|
||||
{
|
||||
$this->properties[$name] = $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns custom properites.
|
||||
* @internal
|
||||
*/
|
||||
public function getProperties(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
public function expandTokens(string $s): string
|
||||
{
|
||||
return strtr($s, $this->placeholders);
|
||||
}
|
||||
|
||||
|
||||
private function processText(Token $token): void
|
||||
{
|
||||
if ($this->lastAttrValue === '' && $this->context && Helpers::startsWith($this->context, self::CONTEXT_HTML_ATTRIBUTE)) {
|
||||
$this->lastAttrValue = $token->text;
|
||||
}
|
||||
$this->output .= $this->escape($token->text);
|
||||
}
|
||||
|
||||
|
||||
private function processMacroTag(Token $token): void
|
||||
{
|
||||
if ($this->context === self::CONTEXT_HTML_TAG || $this->context && Helpers::startsWith($this->context, self::CONTEXT_HTML_ATTRIBUTE)) {
|
||||
$this->lastAttrValue = true;
|
||||
}
|
||||
|
||||
$isRightmost = !isset($this->tokens[$this->position + 1])
|
||||
|| substr($this->tokens[$this->position + 1]->text, 0, 1) === "\n";
|
||||
|
||||
if ($token->closing) {
|
||||
$this->closeMacro($token->name, $token->value, $token->modifiers, $isRightmost);
|
||||
} else {
|
||||
if (!$token->empty && ($this->flags[$token->name] ?? null) & IMacro::AUTO_EMPTY) {
|
||||
$pos = $this->position;
|
||||
while (($t = $this->tokens[++$pos] ?? null)
|
||||
&& ($t->type !== Token::MACRO_TAG || $t->name !== $token->name)
|
||||
&& ($t->type !== Token::HTML_ATTRIBUTE_BEGIN || $t->name !== Parser::N_PREFIX . $token->name));
|
||||
$token->empty = $t ? !$t->closing : true;
|
||||
}
|
||||
$node = $this->openMacro($token->name, $token->value, $token->modifiers, $isRightmost);
|
||||
if ($token->empty) {
|
||||
if ($node->empty) {
|
||||
throw new CompileException("Unexpected /} in tag {$token->text}");
|
||||
}
|
||||
$this->closeMacro($token->name, null, null, $isRightmost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function processHtmlTagBegin(Token $token): void
|
||||
{
|
||||
if ($token->closing) {
|
||||
while ($this->htmlNode) {
|
||||
if (strcasecmp($this->htmlNode->name, $token->name) === 0) {
|
||||
break;
|
||||
}
|
||||
if ($this->htmlNode->macroAttrs) {
|
||||
throw new CompileException("Unexpected </$token->name>, expecting " . self::printEndTag($this->htmlNode));
|
||||
}
|
||||
$this->htmlNode = $this->htmlNode->parentNode;
|
||||
}
|
||||
if (!$this->htmlNode) {
|
||||
$this->htmlNode = new HtmlNode($token->name);
|
||||
}
|
||||
$this->htmlNode->closing = true;
|
||||
$this->htmlNode->endLine = $this->getLine();
|
||||
$this->context = self::CONTEXT_HTML_TEXT;
|
||||
|
||||
} elseif ($token->text === '<!--') {
|
||||
$this->context = self::CONTEXT_HTML_COMMENT;
|
||||
|
||||
} elseif ($token->text === '<?' || $token->text === '<!') {
|
||||
$this->context = self::CONTEXT_HTML_BOGUS_COMMENT;
|
||||
|
||||
} else {
|
||||
$this->htmlNode = new HtmlNode($token->name, $this->htmlNode);
|
||||
$this->htmlNode->startLine = $this->getLine();
|
||||
$this->context = self::CONTEXT_HTML_TAG;
|
||||
}
|
||||
$this->tagOffset = strlen($this->output);
|
||||
$this->output .= $this->escape($token->text);
|
||||
}
|
||||
|
||||
|
||||
private function processHtmlTagEnd(Token $token): void
|
||||
{
|
||||
if (in_array($this->context, [self::CONTEXT_HTML_COMMENT, self::CONTEXT_HTML_BOGUS_COMMENT], true)) {
|
||||
$this->output .= $token->text;
|
||||
$this->context = self::CONTEXT_HTML_TEXT;
|
||||
return;
|
||||
}
|
||||
|
||||
$htmlNode = $this->htmlNode;
|
||||
$end = '';
|
||||
|
||||
if (!$htmlNode->closing) {
|
||||
$htmlNode->empty = strpos($token->text, '/') !== false;
|
||||
if (in_array($this->contentType, [self::CONTENT_HTML, self::CONTENT_XHTML], true)) {
|
||||
$emptyElement = isset(Helpers::$emptyElements[strtolower($htmlNode->name)]);
|
||||
$htmlNode->empty = $htmlNode->empty || $emptyElement;
|
||||
if ($htmlNode->empty) { // auto-correct
|
||||
$space = substr(strstr($token->text, '>'), 1);
|
||||
if ($emptyElement) {
|
||||
$token->text = ($this->contentType === self::CONTENT_XHTML ? ' />' : '>') . $space;
|
||||
} else {
|
||||
$token->text = '>';
|
||||
$end = "</$htmlNode->name>" . $space;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($htmlNode->macroAttrs) {
|
||||
$html = substr($this->output, $this->tagOffset) . $token->text;
|
||||
$this->output = substr($this->output, 0, $this->tagOffset);
|
||||
$this->writeAttrsMacro($html);
|
||||
} else {
|
||||
$this->output .= $token->text . $end;
|
||||
}
|
||||
|
||||
if ($htmlNode->empty) {
|
||||
$htmlNode->closing = true;
|
||||
if ($htmlNode->macroAttrs) {
|
||||
$this->writeAttrsMacro($end);
|
||||
}
|
||||
}
|
||||
|
||||
$this->context = self::CONTEXT_HTML_TEXT;
|
||||
|
||||
if ($htmlNode->closing) {
|
||||
$this->htmlNode = $this->htmlNode->parentNode;
|
||||
|
||||
} elseif (
|
||||
(($lower = strtolower($htmlNode->name)) === 'script' || $lower === 'style')
|
||||
&& (!isset($htmlNode->attrs['type']) || preg_match('#(java|j|ecma|live)script|json|css#i', $htmlNode->attrs['type']))
|
||||
) {
|
||||
$this->context = $lower === 'script' ? self::CONTEXT_HTML_JS : self::CONTEXT_HTML_CSS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function processHtmlAttributeBegin(Token $token): void
|
||||
{
|
||||
if (Helpers::startsWith($token->name, Parser::N_PREFIX)) {
|
||||
$name = substr($token->name, strlen(Parser::N_PREFIX));
|
||||
if (isset($this->htmlNode->macroAttrs[$name])) {
|
||||
throw new CompileException("Found multiple attributes $token->name.");
|
||||
|
||||
} elseif ($this->macroNode && $this->macroNode->htmlNode === $this->htmlNode) {
|
||||
throw new CompileException("n:attributes must not appear inside macro; found $token->name inside {{$this->macroNode->name}}.");
|
||||
}
|
||||
$this->htmlNode->macroAttrs[$name] = $token->value;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lastAttrValue = &$this->htmlNode->attrs[$token->name];
|
||||
$this->output .= $this->escape($token->text);
|
||||
|
||||
$lower = strtolower($token->name);
|
||||
if (in_array($token->value, ['"', "'"], true)) {
|
||||
$this->lastAttrValue = '';
|
||||
$this->context = self::CONTEXT_HTML_ATTRIBUTE;
|
||||
if (in_array($this->contentType, [self::CONTENT_HTML, self::CONTENT_XHTML], true)) {
|
||||
if (Helpers::startsWith($lower, 'on')) {
|
||||
$this->context = self::CONTEXT_HTML_ATTRIBUTE_JS;
|
||||
} elseif ($lower === 'style') {
|
||||
$this->context = self::CONTEXT_HTML_ATTRIBUTE_CSS;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->lastAttrValue = $token->value;
|
||||
$this->context = self::CONTEXT_HTML_TAG;
|
||||
}
|
||||
|
||||
if (
|
||||
in_array($this->contentType, [self::CONTENT_HTML, self::CONTENT_XHTML], true)
|
||||
&& (in_array($lower, ['href', 'src', 'action', 'formaction'], true)
|
||||
|| ($lower === 'data' && strtolower($this->htmlNode->name) === 'object'))
|
||||
) {
|
||||
$this->context = $this->context === self::CONTEXT_HTML_TAG ? self::CONTEXT_HTML_ATTRIBUTE_UNQUOTED_URL : self::CONTEXT_HTML_ATTRIBUTE_URL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function processHtmlAttributeEnd(Token $token): void
|
||||
{
|
||||
$this->context = self::CONTEXT_HTML_TAG;
|
||||
$this->output .= $token->text;
|
||||
}
|
||||
|
||||
|
||||
private function processComment(Token $token): void
|
||||
{
|
||||
$leftOfs = ($tmp = strrpos($this->output, "\n")) === false ? 0 : $tmp + 1;
|
||||
$isLeftmost = trim(substr($this->output, $leftOfs)) === '';
|
||||
$isRightmost = substr($token->text, -1) === "\n";
|
||||
if ($isLeftmost && $isRightmost) {
|
||||
$this->output = substr($this->output, 0, $leftOfs);
|
||||
} else {
|
||||
$this->output .= substr($token->text, strlen(rtrim($token->text, "\n")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function escape(string $s): string
|
||||
{
|
||||
return substr(str_replace('<?', '<<?php ?>?', $s . '?'), 0, -1);
|
||||
}
|
||||
|
||||
|
||||
/********************* macros ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Generates code for {macro ...} to the output.
|
||||
* @internal
|
||||
*/
|
||||
public function openMacro(string $name, string $args = null, string $modifiers = null, bool $isRightmost = false, string $nPrefix = null): MacroNode
|
||||
{
|
||||
$node = $this->expandMacro($name, $args, $modifiers, $nPrefix);
|
||||
if ($node->empty) {
|
||||
$this->writeCode((string) $node->openingCode, $node->replaced, $isRightmost);
|
||||
if ($node->prefix && $node->prefix !== MacroNode::PREFIX_TAG) {
|
||||
$this->htmlNode->attrCode .= $node->attrCode;
|
||||
}
|
||||
} else {
|
||||
$this->macroNode = $node;
|
||||
$node->saved = [&$this->output, $isRightmost];
|
||||
$this->output = &$node->content;
|
||||
$this->output = '';
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates code for {/macro ...} to the output.
|
||||
* @internal
|
||||
*/
|
||||
public function closeMacro(string $name, string $args = null, string $modifiers = null, bool $isRightmost = false, string $nPrefix = null): MacroNode
|
||||
{
|
||||
$node = $this->macroNode;
|
||||
|
||||
if (
|
||||
!$node
|
||||
|| ($node->name !== $name && $name !== '')
|
||||
|| $modifiers
|
||||
|| ($args && $node->args && !Helpers::startsWith("$node->args ", "$args "))
|
||||
|| $nPrefix !== $node->prefix
|
||||
) {
|
||||
$name = $nPrefix
|
||||
? "</{$this->htmlNode->name}> for " . Parser::N_PREFIX . implode(' and ' . Parser::N_PREFIX, array_keys($this->htmlNode->macroAttrs))
|
||||
: '{/' . $name . ($args ? ' ' . $args : '') . $modifiers . '}';
|
||||
throw new CompileException("Unexpected $name" . ($node ? ', expecting ' . self::printEndTag($node->prefix ? $this->htmlNode : $node) : ''));
|
||||
}
|
||||
|
||||
$this->macroNode = $node->parentNode;
|
||||
if (!$node->args) {
|
||||
$node->setArgs($args);
|
||||
}
|
||||
|
||||
if ($node->prefix === MacroNode::PREFIX_NONE) {
|
||||
$parts = explode($node->htmlNode->innerMarker, $node->content);
|
||||
if (count($parts) === 3) { // markers may be destroyed by inner macro
|
||||
$node->innerContent = $parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
$node->closing = true;
|
||||
$node->endLine = $node->prefix ? $node->htmlNode->endLine : $this->getLine();
|
||||
$node->macro->nodeClosed($node);
|
||||
|
||||
if (isset($parts[1]) && $node->innerContent !== $parts[1]) {
|
||||
$node->content = implode($node->htmlNode->innerMarker, [$parts[0], $node->innerContent, $parts[2]]);
|
||||
}
|
||||
|
||||
if ($node->prefix && $node->prefix !== MacroNode::PREFIX_TAG) {
|
||||
$this->htmlNode->attrCode .= $node->attrCode;
|
||||
}
|
||||
$this->output = &$node->saved[0];
|
||||
$this->writeCode((string) $node->openingCode, $node->replaced, $node->saved[1]);
|
||||
$this->output .= $node->content;
|
||||
$this->writeCode((string) $node->closingCode, $node->replaced, $isRightmost);
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
private function writeCode(string $code, ?bool $isReplaced, ?bool $isRightmost): void
|
||||
{
|
||||
if ($isRightmost) {
|
||||
$leftOfs = ($tmp = strrpos($this->output, "\n")) === false ? 0 : $tmp + 1;
|
||||
$isLeftmost = trim(substr($this->output, $leftOfs)) === '';
|
||||
if ($isReplaced === null) {
|
||||
$isReplaced = preg_match('#<\?php.*\secho\s#As', $code);
|
||||
}
|
||||
if ($isLeftmost && !$isReplaced) {
|
||||
$this->output = substr($this->output, 0, $leftOfs); // alone macro without output -> remove indentation
|
||||
if (substr($code, -2) !== '?>') {
|
||||
$code .= '<?php ?>'; // consume new line
|
||||
}
|
||||
} elseif (substr($code, -2) === '?>') {
|
||||
$code .= "\n"; // double newline to avoid newline eating by PHP
|
||||
}
|
||||
}
|
||||
$this->output .= $code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates code for macro <tag n:attr> to the output.
|
||||
* @internal
|
||||
*/
|
||||
public function writeAttrsMacro(string $html): void
|
||||
{
|
||||
// none-2 none-1 tag-1 tag-2 <el attr-1 attr-2> /tag-2 /tag-1 [none-2] [none-1] inner-2 inner-1
|
||||
// /inner-1 /inner-2 [none-1] [none-2] tag-1 tag-2 </el> /tag-2 /tag-1 /none-1 /none-2
|
||||
$attrs = $this->htmlNode->macroAttrs;
|
||||
$left = $right = [];
|
||||
|
||||
foreach ($this->macros as $name => $foo) {
|
||||
$attrName = MacroNode::PREFIX_INNER . "-$name";
|
||||
if (isset($attrs[$attrName])) {
|
||||
if ($this->htmlNode->closing) {
|
||||
$left[] = function () use ($name) {
|
||||
$this->closeMacro($name, '', null, false, MacroNode::PREFIX_INNER);
|
||||
};
|
||||
} else {
|
||||
array_unshift($right, function () use ($name, $attrs, $attrName) {
|
||||
if ($this->openMacro($name, $attrs[$attrName], null, false, MacroNode::PREFIX_INNER)->empty) {
|
||||
throw new CompileException("Unable to use empty macro as n:$attrName.");
|
||||
}
|
||||
});
|
||||
}
|
||||
unset($attrs[$attrName]);
|
||||
}
|
||||
}
|
||||
|
||||
$innerMarker = '';
|
||||
if ($this->htmlNode->closing) {
|
||||
$left[] = function () {
|
||||
$this->output .= $this->htmlNode->innerMarker;
|
||||
};
|
||||
} else {
|
||||
array_unshift($right, function () use (&$innerMarker) {
|
||||
$this->output .= $innerMarker;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
foreach (array_reverse($this->macros) as $name => $foo) {
|
||||
$attrName = MacroNode::PREFIX_TAG . "-$name";
|
||||
if (isset($attrs[$attrName])) {
|
||||
$left[] = function () use ($name, $attrs, $attrName) {
|
||||
if ($this->openMacro($name, $attrs[$attrName], null, false, MacroNode::PREFIX_TAG)->empty) {
|
||||
throw new CompileException("Unable to use empty macro as n:$attrName.");
|
||||
}
|
||||
};
|
||||
array_unshift($right, function () use ($name) {
|
||||
$this->closeMacro($name, '', null, false, MacroNode::PREFIX_TAG);
|
||||
});
|
||||
unset($attrs[$attrName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->macros as $name => $foo) {
|
||||
if (isset($attrs[$name])) {
|
||||
if ($this->htmlNode->closing) {
|
||||
$right[] = function () use ($name) {
|
||||
$this->closeMacro($name, '', null, false, MacroNode::PREFIX_NONE);
|
||||
};
|
||||
} else {
|
||||
array_unshift($left, function () use ($name, $attrs, &$innerMarker) {
|
||||
$node = $this->openMacro($name, $attrs[$name], null, false, MacroNode::PREFIX_NONE);
|
||||
if ($node->empty) {
|
||||
unset($this->htmlNode->macroAttrs[$name]); // don't call closeMacro
|
||||
} elseif (!$innerMarker) {
|
||||
$this->htmlNode->innerMarker = $innerMarker = '<n:q' . count($this->placeholders) . 'q>';
|
||||
$this->placeholders[$innerMarker] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
unset($attrs[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($attrs) {
|
||||
throw new CompileException(
|
||||
'Unknown attribute ' . Parser::N_PREFIX
|
||||
. implode(' and ' . Parser::N_PREFIX, array_keys($attrs))
|
||||
. (($t = Helpers::getSuggestion(array_keys($this->macros), key($attrs))) ? ', did you mean ' . Parser::N_PREFIX . $t . '?' : '')
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->htmlNode->closing) {
|
||||
$this->htmlNode->attrCode = &$this->placeholders[$uniq = ' n:q' . count($this->placeholders) . 'q'];
|
||||
$html = substr_replace($html, $uniq, strrpos($html, '/>') ?: strrpos($html, '>'), 0);
|
||||
}
|
||||
|
||||
foreach ($left as $func) {
|
||||
$func();
|
||||
}
|
||||
|
||||
$this->output .= $html;
|
||||
|
||||
foreach ($right as $func) {
|
||||
$func();
|
||||
}
|
||||
|
||||
if ($right && substr($this->output, -2) === '?>') {
|
||||
$this->output .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expands macro and returns node & code.
|
||||
* @internal
|
||||
*/
|
||||
public function expandMacro(string $name, string $args, string $modifiers = null, string $nPrefix = null): MacroNode
|
||||
{
|
||||
if (empty($this->macros[$name])) {
|
||||
$hint = (($t = Helpers::getSuggestion(array_keys($this->macros), $name)) ? ", did you mean {{$t}}?" : '')
|
||||
. (in_array($this->context, [self::CONTEXT_HTML_JS, self::CONTEXT_HTML_CSS], true) ? ' (in JavaScript or CSS, try to put a space after bracket or use n:syntax=off)' : '');
|
||||
throw new CompileException("Unknown macro {{$name}}$hint");
|
||||
}
|
||||
|
||||
$modifiers = (string) $modifiers;
|
||||
|
||||
if (strpbrk($name, '=~%^&_')) {
|
||||
if (in_array($this->context, [self::CONTEXT_HTML_ATTRIBUTE_URL, self::CONTEXT_HTML_ATTRIBUTE_UNQUOTED_URL], true)) {
|
||||
if (!Helpers::removeFilter($modifiers, 'nocheck') && !preg_match('#\|datastream(?=\s|\||\z)#i', $modifiers)) {
|
||||
$modifiers .= '|checkurl';
|
||||
}
|
||||
}
|
||||
|
||||
if (!Helpers::removeFilter($modifiers, 'noescape')) {
|
||||
$modifiers .= '|escape';
|
||||
if ($this->context === self::CONTEXT_HTML_JS && $name === '=' && preg_match('#["\'] *\z#', $this->tokens[$this->position - 1]->text)) {
|
||||
throw new CompileException("Do not place {$this->tokens[$this->position]->text} inside quotes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($nPrefix === MacroNode::PREFIX_INNER && !strcasecmp($this->htmlNode->name, 'script')) {
|
||||
$context = [$this->contentType, self::CONTEXT_HTML_JS];
|
||||
} elseif ($nPrefix === MacroNode::PREFIX_INNER && !strcasecmp($this->htmlNode->name, 'style')) {
|
||||
$context = [$this->contentType, self::CONTEXT_HTML_CSS];
|
||||
} elseif ($nPrefix) {
|
||||
$context = [$this->contentType, self::CONTEXT_HTML_TEXT];
|
||||
} else {
|
||||
$context = [$this->contentType, $this->context];
|
||||
}
|
||||
|
||||
foreach (array_reverse($this->macros[$name]) as $macro) {
|
||||
$node = new MacroNode($macro, $name, $args, $modifiers, $this->macroNode, $this->htmlNode, $nPrefix);
|
||||
$node->context = $context;
|
||||
$node->startLine = $nPrefix ? $this->htmlNode->startLine : $this->getLine();
|
||||
if ($macro->nodeOpened($node) !== false) {
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
||||
throw new CompileException('Unknown ' . ($nPrefix
|
||||
? 'attribute ' . Parser::N_PREFIX . ($nPrefix === MacroNode::PREFIX_NONE ? '' : "$nPrefix-") . $name
|
||||
: 'macro {' . $name . ($args ? " $args" : '') . '}'
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
private static function printEndTag($node): string
|
||||
{
|
||||
if ($node instanceof HtmlNode) {
|
||||
return "</{$node->name}> for " . Parser::N_PREFIX
|
||||
. implode(' and ' . Parser::N_PREFIX, array_keys($node->macroAttrs));
|
||||
} else {
|
||||
return "{/$node->name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* HTML element node.
|
||||
*/
|
||||
class HtmlNode
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
/** @var bool */
|
||||
public $empty;
|
||||
|
||||
/** @var array */
|
||||
public $attrs = [];
|
||||
|
||||
/** @var array */
|
||||
public $macroAttrs = [];
|
||||
|
||||
/** @var bool */
|
||||
public $closing = false;
|
||||
|
||||
/** @var HtmlNode */
|
||||
public $parentNode;
|
||||
|
||||
/** @var string */
|
||||
public $attrCode;
|
||||
|
||||
/** @var int position of start tag in source template */
|
||||
public $startLine;
|
||||
|
||||
/** @var int position of end tag in source template */
|
||||
public $endLine;
|
||||
|
||||
/** @var string @internal */
|
||||
public $innerMarker;
|
||||
|
||||
|
||||
public function __construct(string $name, self $parentNode = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->parentNode = $parentNode;
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Macro element node.
|
||||
*/
|
||||
class MacroNode
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public const
|
||||
PREFIX_INNER = 'inner',
|
||||
PREFIX_TAG = 'tag',
|
||||
PREFIX_NONE = 'none';
|
||||
|
||||
/** @var IMacro */
|
||||
public $macro;
|
||||
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
/** @var bool */
|
||||
public $empty = false;
|
||||
|
||||
/** @var string raw arguments */
|
||||
public $args;
|
||||
|
||||
/** @var string raw modifier */
|
||||
public $modifiers;
|
||||
|
||||
/** @var bool */
|
||||
public $closing = false;
|
||||
|
||||
/** @var bool has output? */
|
||||
public $replaced;
|
||||
|
||||
/** @var MacroTokens */
|
||||
public $tokenizer;
|
||||
|
||||
/** @var MacroNode */
|
||||
public $parentNode;
|
||||
|
||||
/** @var string */
|
||||
public $openingCode;
|
||||
|
||||
/** @var string */
|
||||
public $closingCode;
|
||||
|
||||
/** @var string */
|
||||
public $attrCode;
|
||||
|
||||
/** @var string */
|
||||
public $content;
|
||||
|
||||
/** @var string */
|
||||
public $innerContent;
|
||||
|
||||
/** @var \stdClass user data */
|
||||
public $data;
|
||||
|
||||
/** @var HtmlNode closest HTML node */
|
||||
public $htmlNode;
|
||||
|
||||
/** @var array [contentType, context] */
|
||||
public $context;
|
||||
|
||||
/** @var string indicates n:attribute macro and type of prefix (PREFIX_INNER, PREFIX_TAG, PREFIX_NONE) */
|
||||
public $prefix;
|
||||
|
||||
/** @var int position of start tag in source template */
|
||||
public $startLine;
|
||||
|
||||
/** @var int position of end tag in source template */
|
||||
public $endLine;
|
||||
|
||||
/** @internal */
|
||||
public $saved;
|
||||
|
||||
|
||||
public function __construct(IMacro $macro, string $name, string $args = null, string $modifiers = null, self $parentNode = null, HtmlNode $htmlNode = null, string $prefix = null)
|
||||
{
|
||||
$this->macro = $macro;
|
||||
$this->name = (string) $name;
|
||||
$this->modifiers = (string) $modifiers;
|
||||
$this->parentNode = $parentNode;
|
||||
$this->htmlNode = $htmlNode;
|
||||
$this->prefix = $prefix;
|
||||
$this->data = new \stdClass;
|
||||
$this->setArgs($args);
|
||||
}
|
||||
|
||||
|
||||
public function setArgs(?string $args): void
|
||||
{
|
||||
$this->args = (string) $args;
|
||||
$this->tokenizer = new MacroTokens($this->args);
|
||||
}
|
||||
|
||||
|
||||
public function getNotation(): string
|
||||
{
|
||||
return $this->prefix
|
||||
? Parser::N_PREFIX . ($this->prefix === self::PREFIX_NONE ? '' : $this->prefix . '-') . $this->name
|
||||
: '{' . $this->name . '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Macro tag tokenizer.
|
||||
*/
|
||||
class MacroTokens extends TokenIterator
|
||||
{
|
||||
public const
|
||||
T_WHITESPACE = 1,
|
||||
T_COMMENT = 2,
|
||||
T_SYMBOL = 3,
|
||||
T_NUMBER = 4,
|
||||
T_VARIABLE = 5,
|
||||
T_STRING = 6,
|
||||
T_CAST = 7,
|
||||
T_KEYWORD = 8,
|
||||
T_CHAR = 9;
|
||||
|
||||
/** @var int */
|
||||
public $depth = 0;
|
||||
|
||||
/** @var Tokenizer */
|
||||
private static $tokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $input
|
||||
*/
|
||||
public function __construct($input = [])
|
||||
{
|
||||
parent::__construct(is_array($input) ? $input : $this->parse($input));
|
||||
$this->ignored = [self::T_COMMENT, self::T_WHITESPACE];
|
||||
}
|
||||
|
||||
|
||||
public function parse(string $s): array
|
||||
{
|
||||
self::$tokenizer = self::$tokenizer ?: new Tokenizer([
|
||||
self::T_WHITESPACE => '\s+',
|
||||
self::T_COMMENT => '(?s)/\*.*?\*/',
|
||||
self::T_STRING => Parser::RE_STRING,
|
||||
self::T_KEYWORD => '(?:true|false|null|TRUE|FALSE|NULL|INF|NAN|and|or|xor|clone|new|instanceof|return|continue|break)(?![\w\pL_])', // keyword
|
||||
self::T_CAST => '\((?:expand|string|array|int|integer|float|bool|boolean|object)\)', // type casting
|
||||
self::T_VARIABLE => '\$[\w\pL_]+',
|
||||
self::T_NUMBER => '[+-]?[0-9]+(?:\.[0-9]+)?(?:e[0-9]+)?',
|
||||
self::T_SYMBOL => '[\w\pL_]+(?:-+[\w\pL_]+)*',
|
||||
self::T_CHAR => '::|=>|->|\+\+|--|<<|>>|<=>|<=|>=|===|!==|==|!=|<>|&&|\|\||\?\?|\?>|\*\*|\.\.\.|[^"\']', // =>, any char except quotes
|
||||
], 'u');
|
||||
return self::$tokenizer->tokenize($s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends simple token or string (will be parsed).
|
||||
* @return static
|
||||
*/
|
||||
public function append($val, int $position = null)
|
||||
{
|
||||
if ($val != null) { // intentionally @
|
||||
array_splice(
|
||||
$this->tokens,
|
||||
$position === null ? count($this->tokens) : $position,
|
||||
0,
|
||||
is_array($val) ? [$val] : $this->parse($val)
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepends simple token or string (will be parsed).
|
||||
* @return static
|
||||
*/
|
||||
public function prepend($val)
|
||||
{
|
||||
if ($val != null) { // intentionally @
|
||||
array_splice($this->tokens, 0, 0, is_array($val) ? [$val] : $this->parse($val));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads single token (optionally delimited by comma) from string.
|
||||
*/
|
||||
public function fetchWord(): ?string
|
||||
{
|
||||
$words = $this->fetchWords();
|
||||
return $words ? implode(':', $words) : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads single tokens delimited by colon from string.
|
||||
*/
|
||||
public function fetchWords(): array
|
||||
{
|
||||
do {
|
||||
$words[] = $this->joinUntil(self::T_WHITESPACE, ',', ':');
|
||||
} while ($this->nextToken(':'));
|
||||
|
||||
if (count($words) === 1 && ($space = $this->nextValue(self::T_WHITESPACE))
|
||||
&& (($dot = $this->nextValue('.')) || $this->isPrev('.'))) {
|
||||
$words[0] .= $space . $dot . $this->joinUntil(',');
|
||||
}
|
||||
$this->nextToken(',');
|
||||
$this->nextAll(self::T_WHITESPACE, self::T_COMMENT);
|
||||
return $words === [''] ? [] : $words;
|
||||
}
|
||||
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->depth = 0;
|
||||
return parent::reset();
|
||||
}
|
||||
|
||||
|
||||
protected function next(): void
|
||||
{
|
||||
parent::next();
|
||||
if ($this->isCurrent('[', '(', '{')) {
|
||||
$this->depth++;
|
||||
} elseif ($this->isCurrent(']', ')', '}')) {
|
||||
$this->depth--;
|
||||
}
|
||||
}
|
||||
}
|
||||
+462
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Latte parser.
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** @internal regular expression for single & double quoted PHP string */
|
||||
public const RE_STRING = '\'(?:\\\\.|[^\'\\\\])*+\'|"(?:\\\\.|[^"\\\\])*+"';
|
||||
|
||||
/** @internal special HTML attribute prefix */
|
||||
public const N_PREFIX = 'n:';
|
||||
|
||||
/** Context-aware escaping content types */
|
||||
public const
|
||||
CONTENT_HTML = Engine::CONTENT_HTML,
|
||||
CONTENT_XHTML = Engine::CONTENT_XHTML,
|
||||
CONTENT_XML = Engine::CONTENT_XML,
|
||||
CONTENT_TEXT = Engine::CONTENT_TEXT;
|
||||
|
||||
/** @internal states */
|
||||
public const
|
||||
CONTEXT_NONE = 'none',
|
||||
CONTEXT_MACRO = 'macro',
|
||||
CONTEXT_HTML_TEXT = 'htmlText',
|
||||
CONTEXT_HTML_TAG = 'htmlTag',
|
||||
CONTEXT_HTML_ATTRIBUTE = 'htmlAttribute',
|
||||
CONTEXT_HTML_COMMENT = 'htmlComment',
|
||||
CONTEXT_HTML_CDATA = 'htmlCData';
|
||||
|
||||
/** @var string default macro tag syntax */
|
||||
public $defaultSyntax = 'latte';
|
||||
|
||||
/** @var array */
|
||||
public $syntaxes = [
|
||||
'latte' => ['\{(?![\s\'"{}])', '\}'], // {...}
|
||||
'double' => ['\{\{(?![\s\'"{}])', '\}\}'], // {{...}}
|
||||
'off' => ['\{(?=/syntax\})', '\}'], // {/syntax}
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private $delimiters;
|
||||
|
||||
/** @var string source template */
|
||||
private $input;
|
||||
|
||||
/** @var Token[] */
|
||||
private $output;
|
||||
|
||||
/** @var int position on source template */
|
||||
private $offset;
|
||||
|
||||
/** @var array */
|
||||
private $context = [self::CONTEXT_HTML_TEXT, null];
|
||||
|
||||
/** @var string */
|
||||
private $lastHtmlTag;
|
||||
|
||||
/** @var string used by filter() */
|
||||
private $syntaxEndTag;
|
||||
|
||||
/** @var int */
|
||||
private $syntaxEndLevel = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $xmlMode;
|
||||
|
||||
|
||||
/**
|
||||
* Process all {macros} and <tags/>.
|
||||
* @return Token[]
|
||||
*/
|
||||
public function parse(string $input): array
|
||||
{
|
||||
if (Helpers::startsWith($input, "\u{FEFF}")) { // BOM
|
||||
$input = substr($input, 3);
|
||||
}
|
||||
|
||||
$this->input = $input = str_replace("\r\n", "\n", $input);
|
||||
$this->offset = 0;
|
||||
$this->output = [];
|
||||
|
||||
if (!preg_match('##u', $input)) {
|
||||
preg_match('#(?:[\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})*+#A', $input, $m);
|
||||
$this->offset = strlen($m[0]) + 1;
|
||||
throw new \InvalidArgumentException('Template is not valid UTF-8 stream.');
|
||||
}
|
||||
|
||||
$this->setSyntax($this->defaultSyntax);
|
||||
$this->lastHtmlTag = $this->syntaxEndTag = null;
|
||||
|
||||
$tokenCount = 0;
|
||||
while ($this->offset < strlen($input)) {
|
||||
if ($this->{'context' . $this->context[0]}() === false) {
|
||||
break;
|
||||
}
|
||||
while ($tokenCount < count($this->output)) {
|
||||
$this->filter($this->output[$tokenCount++]);
|
||||
}
|
||||
}
|
||||
if ($this->context[0] === self::CONTEXT_MACRO) {
|
||||
throw new CompileException('Malformed macro');
|
||||
}
|
||||
|
||||
if ($this->offset < strlen($input)) {
|
||||
$this->addToken(Token::TEXT, substr($this->input, $this->offset));
|
||||
}
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_HTML_TEXT.
|
||||
*/
|
||||
private function contextHtmlText()
|
||||
{
|
||||
$matches = $this->match('~
|
||||
(?:(?<=\n|^)[ \t]*)?<(?P<closing>/?)(?P<tag>[a-z][a-z0-9:_.-]*)| ## begin of HTML tag <tag </tag - ignores <!DOCTYPE
|
||||
<(?P<htmlcomment>!(?:--(?!>))?|\?)| ## begin of <!, <!--, <!DOCTYPE, <?
|
||||
(?P<macro>' . $this->delimiters[0] . ')
|
||||
~xsi');
|
||||
|
||||
if (!empty($matches['htmlcomment'])) { // <! <?
|
||||
$this->addToken(Token::HTML_TAG_BEGIN, $matches[0]);
|
||||
$end = $matches['htmlcomment'] === '!--' ? '--' : ($matches['htmlcomment'] === '?' && $this->xmlMode ? '\?' : '');
|
||||
$this->setContext(self::CONTEXT_HTML_COMMENT, $end);
|
||||
|
||||
} elseif (!empty($matches['tag'])) { // <tag or </tag
|
||||
$token = $this->addToken(Token::HTML_TAG_BEGIN, $matches[0]);
|
||||
$token->name = $matches['tag'];
|
||||
$token->closing = (bool) $matches['closing'];
|
||||
$this->lastHtmlTag = $matches['closing'] . strtolower($matches['tag']);
|
||||
$this->setContext(self::CONTEXT_HTML_TAG);
|
||||
|
||||
} else {
|
||||
return $this->processMacro($matches);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_HTML_CDATA.
|
||||
*/
|
||||
private function contextHtmlCData()
|
||||
{
|
||||
$matches = $this->match('~
|
||||
</(?P<tag>' . $this->lastHtmlTag . ')(?![a-z0-9:])| ## end HTML tag </tag
|
||||
(?P<macro>' . $this->delimiters[0] . ')
|
||||
~xsi');
|
||||
|
||||
if (!empty($matches['tag'])) { // </tag
|
||||
$token = $this->addToken(Token::HTML_TAG_BEGIN, $matches[0]);
|
||||
$token->name = $this->lastHtmlTag;
|
||||
$token->closing = true;
|
||||
$this->lastHtmlTag = '/' . $this->lastHtmlTag;
|
||||
$this->setContext(self::CONTEXT_HTML_TAG);
|
||||
} else {
|
||||
return $this->processMacro($matches);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_HTML_TAG.
|
||||
*/
|
||||
private function contextHtmlTag()
|
||||
{
|
||||
$matches = $this->match('~
|
||||
(?P<end>\s?/?>)([ \t]*\n)?| ## end of HTML tag
|
||||
(?P<macro>' . $this->delimiters[0] . ')|
|
||||
\s*(?P<attr>[^\s"\'>/={]+)(?:\s*=\s*(?P<value>["\']|[^\s"\'=<>`{]+))? ## beginning of HTML attribute
|
||||
~xsi');
|
||||
|
||||
if (!empty($matches['end'])) { // end of HTML tag />
|
||||
$this->addToken(Token::HTML_TAG_END, $matches[0]);
|
||||
$empty = strpos($matches[0], '/') !== false;
|
||||
$this->setContext(!$this->xmlMode && !$empty && in_array($this->lastHtmlTag, ['script', 'style'], true) ? self::CONTEXT_HTML_CDATA : self::CONTEXT_HTML_TEXT);
|
||||
|
||||
} elseif (isset($matches['attr']) && $matches['attr'] !== '') { // HTML attribute
|
||||
$token = $this->addToken(Token::HTML_ATTRIBUTE_BEGIN, $matches[0]);
|
||||
$token->name = $matches['attr'];
|
||||
$token->value = $matches['value'] ?? '';
|
||||
|
||||
if ($token->value === '"' || $token->value === "'") { // attribute = "'
|
||||
if (Helpers::startsWith($token->name, self::N_PREFIX)) {
|
||||
$token->value = '';
|
||||
if ($m = $this->match('~(.*?)' . $matches['value'] . '~xsi')) {
|
||||
$token->value = $m[1];
|
||||
$token->text .= $m[0];
|
||||
}
|
||||
} else {
|
||||
$this->setContext(self::CONTEXT_HTML_ATTRIBUTE, $matches['value']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return $this->processMacro($matches);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_HTML_ATTRIBUTE.
|
||||
*/
|
||||
private function contextHtmlAttribute()
|
||||
{
|
||||
$matches = $this->match('~
|
||||
(?P<quote>' . $this->context[1] . ')| ## end of HTML attribute
|
||||
(?P<macro>' . $this->delimiters[0] . ')
|
||||
~xsi');
|
||||
|
||||
if (!empty($matches['quote'])) { // (attribute end) '"
|
||||
$this->addToken(Token::HTML_ATTRIBUTE_END, $matches[0]);
|
||||
$this->setContext(self::CONTEXT_HTML_TAG);
|
||||
} else {
|
||||
return $this->processMacro($matches);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_HTML_COMMENT.
|
||||
*/
|
||||
private function contextHtmlComment()
|
||||
{
|
||||
$matches = $this->match('~
|
||||
(?P<htmlcomment>' . $this->context[1] . '>)| ## end of HTML comment
|
||||
(?P<macro>' . $this->delimiters[0] . ')
|
||||
~xsi');
|
||||
|
||||
if (!empty($matches['htmlcomment'])) { // -->
|
||||
$this->addToken(Token::HTML_TAG_END, $matches[0]);
|
||||
$this->setContext(self::CONTEXT_HTML_TEXT);
|
||||
} else {
|
||||
return $this->processMacro($matches);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_NONE.
|
||||
*/
|
||||
private function contextNone()
|
||||
{
|
||||
$matches = $this->match('~
|
||||
(?P<macro>' . $this->delimiters[0] . ')
|
||||
~xsi');
|
||||
return $this->processMacro($matches);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles CONTEXT_MACRO.
|
||||
*/
|
||||
private function contextMacro(): void
|
||||
{
|
||||
$matches = $this->match('~
|
||||
(?P<comment>\\*.*?\\*' . $this->delimiters[1] . '\n{0,2})|
|
||||
(?P<macro>(?>
|
||||
' . self::RE_STRING . '|
|
||||
\{(?>' . self::RE_STRING . '|[^\'"{}])*+\}|
|
||||
[^\'"{}]+
|
||||
)++)
|
||||
' . $this->delimiters[1] . '
|
||||
(?P<rmargin>[ \t]*(?=\n))?
|
||||
~xsiA');
|
||||
|
||||
if (!empty($matches['macro'])) {
|
||||
$token = $this->addToken(Token::MACRO_TAG, $this->context[1][1] . $matches[0]);
|
||||
[$token->name, $token->value, $token->modifiers, $token->empty, $token->closing] = $this->parseMacroTag($matches['macro']);
|
||||
$this->context = $this->context[1][0];
|
||||
|
||||
} elseif (!empty($matches['comment'])) {
|
||||
$this->addToken(Token::COMMENT, $this->context[1][1] . $matches[0]);
|
||||
$this->context = $this->context[1][0];
|
||||
|
||||
} else {
|
||||
throw new CompileException('Malformed macro');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function processMacro(array $matches)
|
||||
{
|
||||
if (!empty($matches['macro'])) { // {macro} or {* *}
|
||||
$this->setContext(self::CONTEXT_MACRO, [$this->context, $matches['macro']]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matches next token.
|
||||
*/
|
||||
private function match(string $re): array
|
||||
{
|
||||
if (!preg_match($re, $this->input, $matches, PREG_OFFSET_CAPTURE, $this->offset)) {
|
||||
if (preg_last_error()) {
|
||||
throw new RegexpException(null, preg_last_error());
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
$value = substr($this->input, $this->offset, $matches[0][1] - $this->offset);
|
||||
if ($value !== '') {
|
||||
$this->addToken(Token::TEXT, $value);
|
||||
}
|
||||
$this->offset = $matches[0][1] + strlen($matches[0][0]);
|
||||
foreach ($matches as $k => $v) {
|
||||
$matches[$k] = $v[0];
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $type Parser::CONTENT_HTML, CONTENT_XHTML, CONTENT_XML or CONTENT_TEXT
|
||||
* @return static
|
||||
*/
|
||||
public function setContentType(string $type)
|
||||
{
|
||||
if (in_array($type, [self::CONTENT_HTML, self::CONTENT_XHTML, self::CONTENT_XML], true)) {
|
||||
$this->setContext(self::CONTEXT_HTML_TEXT);
|
||||
$this->xmlMode = $type === self::CONTENT_XML;
|
||||
} else {
|
||||
$this->setContext(self::CONTEXT_NONE);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function setContext(string $context, $quote = null)
|
||||
{
|
||||
$this->context = [$context, $quote];
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes macro tag delimiters.
|
||||
* @return static
|
||||
*/
|
||||
public function setSyntax(string $type)
|
||||
{
|
||||
$type = $type ?: $this->defaultSyntax;
|
||||
if (isset($this->syntaxes[$type])) {
|
||||
$this->setDelimiters($this->syntaxes[$type][0], $this->syntaxes[$type][1]);
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Unknown syntax '$type'");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes macro tag delimiters (as regular expression).
|
||||
* @return static
|
||||
*/
|
||||
public function setDelimiters(string $left, string $right)
|
||||
{
|
||||
$this->delimiters = [$left, $right];
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses macro tag to name, arguments a modifiers parts.
|
||||
* @param string $tag {name arguments | modifiers}
|
||||
* @internal
|
||||
*/
|
||||
public function parseMacroTag(string $tag): ?array
|
||||
{
|
||||
if (!preg_match('~^
|
||||
(?P<closing>/?)
|
||||
(
|
||||
(?P<name>\?|[a-z]\w*+(?:[.:]\w+)*+(?!::|\(|\\\\))| ## ?, name, /name, but not function( or class:: or namespace\
|
||||
(?P<shortname>[=\~#%^&_]?) ## expression, =expression, ...
|
||||
)(?P<args>(?:' . self::RE_STRING . '|[^\'"])*?)
|
||||
(?P<modifiers>(?<!\|)\|[a-z](?P<modArgs>(?:' . self::RE_STRING . '|(?:\((?P>modArgs)\))|[^\'"/()]|/(?=.))*+))?
|
||||
(?P<empty>/?\z)
|
||||
()\z~isx', $tag, $match)) {
|
||||
if (preg_last_error()) {
|
||||
throw new RegexpException(null, preg_last_error());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if ($match['name'] === '') {
|
||||
$match['name'] = $match['shortname'] ?: ($match['closing'] ? '' : '=');
|
||||
}
|
||||
return [$match['name'], trim($match['args']), $match['modifiers'], (bool) $match['empty'], (bool) $match['closing']];
|
||||
}
|
||||
|
||||
|
||||
private function addToken(string $type, string $text): Token
|
||||
{
|
||||
$this->output[] = $token = new Token;
|
||||
$token->type = $type;
|
||||
$token->text = $text;
|
||||
$token->line = $this->getLine() - substr_count(ltrim($text), "\n");
|
||||
return $token;
|
||||
}
|
||||
|
||||
|
||||
public function getLine(): int
|
||||
{
|
||||
return $this->offset
|
||||
? substr_count(substr($this->input, 0, $this->offset - 1), "\n") + 1
|
||||
: 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process low-level macros.
|
||||
*/
|
||||
protected function filter(Token $token): void
|
||||
{
|
||||
if ($token->type === Token::MACRO_TAG && $token->name === '/syntax') {
|
||||
$this->setSyntax($this->defaultSyntax);
|
||||
$token->type = Token::COMMENT;
|
||||
|
||||
} elseif ($token->type === Token::MACRO_TAG && $token->name === 'syntax') {
|
||||
$this->setSyntax($token->value);
|
||||
$token->type = Token::COMMENT;
|
||||
|
||||
} elseif ($token->type === Token::HTML_ATTRIBUTE_BEGIN && $token->name === 'n:syntax') {
|
||||
$this->setSyntax($token->value);
|
||||
$this->syntaxEndTag = $this->lastHtmlTag;
|
||||
$this->syntaxEndLevel = 1;
|
||||
$token->type = Token::COMMENT;
|
||||
|
||||
} elseif ($token->type === Token::HTML_TAG_BEGIN && $this->lastHtmlTag === $this->syntaxEndTag) {
|
||||
$this->syntaxEndLevel++;
|
||||
|
||||
} elseif ($token->type === Token::HTML_TAG_END && $this->lastHtmlTag === ('/' . $this->syntaxEndTag) && --$this->syntaxEndLevel === 0) {
|
||||
$this->setSyntax($this->defaultSyntax);
|
||||
|
||||
} elseif ($token->type === Token::MACRO_TAG && $token->name === 'contentType') {
|
||||
if (strpos($token->value, 'html') !== false) {
|
||||
$this->setContentType(self::CONTENT_HTML);
|
||||
} elseif (strpos($token->value, 'xml') !== false) {
|
||||
$this->setContentType(self::CONTENT_XML);
|
||||
} else {
|
||||
$this->setContentType(self::CONTENT_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* PHP helpers.
|
||||
* @internal
|
||||
*/
|
||||
class PhpHelpers
|
||||
{
|
||||
|
||||
/**
|
||||
* Optimizes code readability.
|
||||
*/
|
||||
public static function reformatCode(string $source): string
|
||||
{
|
||||
$res = $php = '';
|
||||
$lastChar = ';';
|
||||
$tokens = new \ArrayIterator(token_get_all($source));
|
||||
$level = $openLevel = 0;
|
||||
$lineLength = 100;
|
||||
|
||||
foreach ($tokens as $n => $token) {
|
||||
if (is_array($token)) {
|
||||
[$name, $token] = ($tmp = $token);
|
||||
if ($name === T_INLINE_HTML) {
|
||||
$res .= $token;
|
||||
|
||||
} elseif ($name === T_OPEN_TAG) {
|
||||
$openLevel = $level;
|
||||
|
||||
} elseif ($name === T_CLOSE_TAG) {
|
||||
$next = $tokens[$n + 1] ?? null;
|
||||
if (is_array($next) && $next[0] === T_OPEN_TAG) { // remove ?)<?php
|
||||
if (!strspn($lastChar, ';{}:/')) {
|
||||
$php = rtrim($php) . ($lastChar = ';') . "\n" . str_repeat("\t", $level);
|
||||
} elseif (substr($next[1], -1) === "\n") {
|
||||
$php .= "\n" . str_repeat("\t", $level);
|
||||
}
|
||||
$tokens->next();
|
||||
|
||||
} else {
|
||||
if (trim($php) !== '' || substr($res, -1) === '<') { // skip <?php ?) but preserve <<?php
|
||||
$inline = strpos($php, "\n") === false && strlen($res) - strrpos($res, "\n") < $lineLength;
|
||||
$res .= '<?php' . ($inline ? ' ' : "\n" . str_repeat("\t", $openLevel));
|
||||
if (is_array($next) && strpos($next[1], "\n") === false) {
|
||||
$token = rtrim($token, "\n");
|
||||
} else {
|
||||
$php = rtrim($php, "\t");
|
||||
}
|
||||
$res .= $php . $token;
|
||||
}
|
||||
$php = '';
|
||||
$lastChar = ';';
|
||||
}
|
||||
|
||||
} elseif ($name === T_ELSE || $name === T_ELSEIF) {
|
||||
if ($tokens[$n + 1] === ':' && $lastChar === '}') {
|
||||
$php .= ';'; // semicolon needed in if(): ... if() ... else:
|
||||
}
|
||||
$lastChar = '';
|
||||
$php .= $token;
|
||||
|
||||
} elseif ($name === T_DOC_COMMENT || $name === T_COMMENT) {
|
||||
$php .= preg_replace("#\n[ \t]*+(?!\n)#", "\n" . str_repeat("\t", $level), $token);
|
||||
|
||||
} elseif ($name === T_WHITESPACE) {
|
||||
$prev = $tokens[$n - 1];
|
||||
$lines = substr_count($token, "\n");
|
||||
if ($prev === '{' || $prev === '}' || $prev === ';' || $lines) {
|
||||
$token = str_repeat("\n", max(1, $lines)) . str_repeat("\t", $level); // indent last line
|
||||
} elseif ($prev[0] === T_OPEN_TAG) {
|
||||
$token = '';
|
||||
}
|
||||
$php .= $token;
|
||||
|
||||
} else {
|
||||
if (in_array($name, [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true)) {
|
||||
$level++;
|
||||
}
|
||||
$lastChar = '';
|
||||
$php .= $token;
|
||||
}
|
||||
} else {
|
||||
if ($token === '{' || $token === '[') {
|
||||
$level++;
|
||||
} elseif ($token === '}' || $token === ']') {
|
||||
$level--;
|
||||
$php .= "\x08";
|
||||
} elseif ($token === ';' && !(($tokens[$n + 1][0] ?? null) === T_WHITESPACE)) {
|
||||
$token .= "\n" . str_repeat("\t", $level); // indent last line
|
||||
}
|
||||
$lastChar = $token;
|
||||
$php .= $token;
|
||||
}
|
||||
}
|
||||
|
||||
if ($php) {
|
||||
$res .= "<?php\n" . str_repeat("\t", $openLevel) . $php;
|
||||
}
|
||||
$res = str_replace(["\t\x08", "\x08"], '', $res);
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
public static function dump($value): string
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$s = "[\n";
|
||||
foreach ($value as $k => $v) {
|
||||
$v = is_array($v) && (!$v || array_keys($v) === range(0, count($v) - 1))
|
||||
? '[' . implode(', ', array_map(function ($s): string { return var_export($s, true); }, $v)) . ']'
|
||||
: var_export($v, true);
|
||||
$s .= "\t\t" . var_export($k, true) . ' => ' . $v . ",\n";
|
||||
}
|
||||
return $s . "\t]";
|
||||
}
|
||||
return var_export($value, true);
|
||||
}
|
||||
}
|
||||
+507
@@ -0,0 +1,507 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* PHP code generator helpers.
|
||||
*/
|
||||
class PhpWriter
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** @var MacroTokens */
|
||||
private $tokens;
|
||||
|
||||
/** @var string */
|
||||
private $modifiers;
|
||||
|
||||
/** @var array|null */
|
||||
private $context;
|
||||
|
||||
|
||||
public static function using(MacroNode $node): self
|
||||
{
|
||||
$me = new static($node->tokenizer, null, $node->context);
|
||||
$me->modifiers = &$node->modifiers;
|
||||
return $me;
|
||||
}
|
||||
|
||||
|
||||
public function __construct(MacroTokens $tokens, string $modifiers = null, array $context = null)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
$this->modifiers = $modifiers;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expands %node.word, %node.array, %node.args, %escape(), %modify(), %var, %raw, %word in code.
|
||||
*/
|
||||
public function write(string $mask, ...$args): string
|
||||
{
|
||||
$mask = preg_replace('#%(node|\d+)\.#', '%$1_', $mask);
|
||||
$mask = preg_replace_callback('#%escape(\(([^()]*+|(?1))+\))#', function ($m) {
|
||||
return $this->escapePass(new MacroTokens(substr($m[1], 1, -1)))->joinAll();
|
||||
}, $mask);
|
||||
$mask = preg_replace_callback('#%modify(Content)?(\(([^()]*+|(?2))+\))#', function ($m) {
|
||||
return $this->formatModifiers(substr($m[2], 1, -1), (bool) $m[1]);
|
||||
}, $mask);
|
||||
|
||||
$pos = $this->tokens->position;
|
||||
$word = null;
|
||||
if (strpos($mask, '%node_word') !== false) {
|
||||
$word = $this->tokens->fetchWord();
|
||||
if ($word === null) {
|
||||
throw new CompileException('Invalid content of macro');
|
||||
}
|
||||
}
|
||||
|
||||
$code = preg_replace_callback('#([,+]\s*)?%(node_|\d+_|)(word|var|raw|array|args)(\?)?(\s*\+\s*)?()#',
|
||||
function ($m) use ($word, &$args) {
|
||||
[, $l, $source, $format, $cond, $r] = $m;
|
||||
|
||||
switch ($source) {
|
||||
case 'node_':
|
||||
$arg = $word; break;
|
||||
case '':
|
||||
$arg = current($args); next($args); break;
|
||||
default:
|
||||
$arg = $args[(int) $source]; break;
|
||||
}
|
||||
|
||||
switch ($format) {
|
||||
case 'word':
|
||||
$code = $this->formatWord($arg); break;
|
||||
case 'args':
|
||||
$code = $this->formatArgs(); break;
|
||||
case 'array':
|
||||
$code = $this->formatArray();
|
||||
$code = $cond && $code === '[]' ? '' : $code; break;
|
||||
case 'var':
|
||||
$code = var_export($arg, true); break;
|
||||
case 'raw':
|
||||
$code = (string) $arg; break;
|
||||
}
|
||||
|
||||
if ($cond && $code === '') {
|
||||
return $r ? $l : $r;
|
||||
} else {
|
||||
return $l . $code . $r;
|
||||
}
|
||||
}, $mask);
|
||||
|
||||
$this->tokens->position = $pos;
|
||||
return $code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats modifiers calling.
|
||||
*/
|
||||
public function formatModifiers(string $var, bool $isContent = false): string
|
||||
{
|
||||
$tokens = new MacroTokens(ltrim($this->modifiers, '|'));
|
||||
$tokens = $this->preprocess($tokens);
|
||||
$tokens = $this->modifierPass($tokens, $var, $isContent);
|
||||
$tokens = $this->quotingPass($tokens);
|
||||
return $tokens->joinAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats macro arguments to PHP code. (It advances tokenizer to the end as a side effect.)
|
||||
*/
|
||||
public function formatArgs(MacroTokens $tokens = null): string
|
||||
{
|
||||
$tokens = $this->preprocess($tokens);
|
||||
$tokens = $this->quotingPass($tokens);
|
||||
return $tokens->joinAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats macro arguments to PHP array. (It advances tokenizer to the end as a side effect.)
|
||||
*/
|
||||
public function formatArray(MacroTokens $tokens = null): string
|
||||
{
|
||||
$tokens = $this->preprocess($tokens);
|
||||
$tokens = $this->expandCastPass($tokens);
|
||||
$tokens = $this->quotingPass($tokens);
|
||||
return $tokens->joinAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats parameter to PHP string.
|
||||
*/
|
||||
public function formatWord(string $s): string
|
||||
{
|
||||
return (is_numeric($s) || preg_match('#^\$|[\'"]|^(true|TRUE)\z|^(false|FALSE)\z|^(null|NULL)\z|^[\w\\\\]{3,}::[A-Z0-9_]{2,}\z#', $s))
|
||||
? $this->formatArgs(new MacroTokens($s))
|
||||
: '"' . $s . '"';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Preprocessor for tokens. (It advances tokenizer to the end as a side effect.)
|
||||
*/
|
||||
public function preprocess(MacroTokens $tokens = null): MacroTokens
|
||||
{
|
||||
$tokens = $tokens === null ? $this->tokens : $tokens;
|
||||
$this->validateTokens($tokens);
|
||||
$tokens = $this->removeCommentsPass($tokens);
|
||||
$tokens = $this->shortTernaryPass($tokens);
|
||||
$tokens = $this->inlineModifierPass($tokens);
|
||||
$tokens = $this->inOperatorPass($tokens);
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws CompileException
|
||||
*/
|
||||
public function validateTokens(MacroTokens $tokens): void
|
||||
{
|
||||
$brackets = [];
|
||||
$pos = $tokens->position;
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent('?>')) {
|
||||
throw new CompileException('Forbidden ?> inside macro');
|
||||
|
||||
} elseif ($tokens->isCurrent('(', '[', '{')) {
|
||||
static $counterpart = ['(' => ')', '[' => ']', '{' => '}'];
|
||||
$brackets[] = $counterpart[$tokens->currentValue()];
|
||||
|
||||
} elseif ($tokens->isCurrent(')', ']', '}') && $tokens->currentValue() !== array_pop($brackets)) {
|
||||
throw new CompileException('Unexpected ' . $tokens->currentValue());
|
||||
|
||||
} elseif (
|
||||
$tokens->isCurrent('function', 'class', 'interface', 'trait')
|
||||
&& $tokens->isNext($tokens::T_SYMBOL, '&')
|
||||
|| $tokens->isCurrent('return', 'yield')
|
||||
&& !$brackets
|
||||
) {
|
||||
throw new CompileException("Forbidden keyword '{$tokens->currentValue()}' inside macro.");
|
||||
}
|
||||
}
|
||||
if ($brackets) {
|
||||
throw new CompileException('Missing ' . array_pop($brackets));
|
||||
}
|
||||
$tokens->position = $pos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes PHP comments.
|
||||
*/
|
||||
public function removeCommentsPass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
$res = new MacroTokens;
|
||||
while ($tokens->nextToken()) {
|
||||
if (!$tokens->isCurrent($tokens::T_COMMENT)) {
|
||||
$res->append($tokens->currentToken());
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simplified ternary expressions without third part.
|
||||
*/
|
||||
public function shortTernaryPass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
$res = new MacroTokens;
|
||||
$inTernary = [];
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent('?')) {
|
||||
$inTernary[] = $tokens->depth;
|
||||
|
||||
} elseif ($tokens->isCurrent(':')) {
|
||||
array_pop($inTernary);
|
||||
|
||||
} elseif ($tokens->isCurrent(',', ')', ']', '|') && end($inTernary) === $tokens->depth + $tokens->isCurrent(')', ']')) {
|
||||
$res->append(' : null');
|
||||
array_pop($inTernary);
|
||||
}
|
||||
$res->append($tokens->currentToken());
|
||||
}
|
||||
|
||||
if ($inTernary) {
|
||||
$res->append(' : null');
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pseudocast (expand).
|
||||
*/
|
||||
public function expandCastPass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
$res = new MacroTokens('[');
|
||||
$expand = null;
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent('(expand)') && $tokens->depth === 0) {
|
||||
$expand = true;
|
||||
$res->append('],');
|
||||
} elseif ($expand && $tokens->isCurrent(',') && !$tokens->depth) {
|
||||
$expand = false;
|
||||
$res->append(', [');
|
||||
} else {
|
||||
$res->append($tokens->currentToken());
|
||||
}
|
||||
}
|
||||
|
||||
if ($expand === null) {
|
||||
$res->append(']');
|
||||
} else {
|
||||
$res->prepend('array_merge(')->append($expand ? ', [])' : '])');
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quotes symbols to strings.
|
||||
*/
|
||||
public function quotingPass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
$res = new MacroTokens;
|
||||
while ($tokens->nextToken()) {
|
||||
$res->append($tokens->isCurrent($tokens::T_SYMBOL)
|
||||
&& (!$tokens->isPrev() || $tokens->isPrev(',', '(', '[', '=>', ':', '?', '.', '<', '>', '<=', '>=', '===', '!==', '==', '!=', '<>', '&&', '||', '=', 'and', 'or', 'xor', '??'))
|
||||
&& (!$tokens->isNext() || $tokens->isNext(',', ';', ')', ']', '=>', ':', '?', '.', '<', '>', '<=', '>=', '===', '!==', '==', '!=', '<>', '&&', '||', 'and', 'or', 'xor', '??'))
|
||||
&& !preg_match('#^[A-Z_][A-Z0-9_]{2,}$#', $tokens->currentValue())
|
||||
? "'" . $tokens->currentValue() . "'"
|
||||
: $tokens->currentToken()
|
||||
);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Syntax $entry in [item1, item2].
|
||||
*/
|
||||
public function inOperatorPass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent($tokens::T_VARIABLE)) {
|
||||
$start = $tokens->position;
|
||||
$depth = $tokens->depth;
|
||||
$expr = $arr = [];
|
||||
|
||||
$expr[] = $tokens->currentToken();
|
||||
while ($tokens->isNext($tokens::T_VARIABLE, $tokens::T_SYMBOL, $tokens::T_NUMBER, $tokens::T_STRING, '[', ']', '(', ')', '->')
|
||||
&& !$tokens->isNext('in')) {
|
||||
$expr[] = $tokens->nextToken();
|
||||
}
|
||||
|
||||
if ($depth === $tokens->depth && $tokens->nextValue('in') && ($arr[] = $tokens->nextToken('['))) {
|
||||
while ($tokens->isNext()) {
|
||||
$arr[] = $tokens->nextToken();
|
||||
if ($tokens->isCurrent(']') && $tokens->depth === $depth) {
|
||||
$new = array_merge($tokens->parse('in_array('), $expr, $tokens->parse(', '), $arr, $tokens->parse(', true)'));
|
||||
array_splice($tokens->tokens, $start, $tokens->position - $start + 1, $new);
|
||||
$tokens->position = $start + count($new) - 1;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$tokens->position = $start;
|
||||
}
|
||||
}
|
||||
return $tokens->reset();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process inline filters ($var|filter)
|
||||
*/
|
||||
public function inlineModifierPass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
$result = new MacroTokens;
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent('(', '[')) {
|
||||
$result->tokens = array_merge($result->tokens, $this->inlineModifierInner($tokens));
|
||||
} else {
|
||||
$result->append($tokens->currentToken());
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
private function inlineModifierInner(MacroTokens $tokens): array
|
||||
{
|
||||
$isFunctionOrArray = $tokens->isPrev($tokens::T_VARIABLE, $tokens::T_SYMBOL) || $tokens->isCurrent('[');
|
||||
$result = new MacroTokens;
|
||||
$args = new MacroTokens;
|
||||
$modifiers = new MacroTokens;
|
||||
$current = $args;
|
||||
$anyModifier = false;
|
||||
$result->append($tokens->currentToken());
|
||||
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent('(', '[')) {
|
||||
$current->tokens = array_merge($current->tokens, $this->inlineModifierInner($tokens));
|
||||
|
||||
} elseif ($current !== $modifiers && $tokens->isCurrent('|')) {
|
||||
$anyModifier = true;
|
||||
$current = $modifiers;
|
||||
|
||||
} elseif ($tokens->isCurrent(')', ']') || ($isFunctionOrArray && $tokens->isCurrent(','))) {
|
||||
$partTokens = count($modifiers->tokens)
|
||||
? $this->modifierPass($modifiers, $args->tokens)->tokens
|
||||
: $args->tokens;
|
||||
$result->tokens = array_merge($result->tokens, $partTokens);
|
||||
if ($tokens->isCurrent(',')) {
|
||||
$result->append($tokens->currentToken());
|
||||
$args = new MacroTokens;
|
||||
$modifiers = new MacroTokens;
|
||||
$current = $args;
|
||||
continue;
|
||||
} elseif ($isFunctionOrArray || !$anyModifier) {
|
||||
$result->append($tokens->currentToken());
|
||||
} else {
|
||||
array_shift($result->tokens);
|
||||
}
|
||||
return $result->tokens;
|
||||
|
||||
} else {
|
||||
$current->append($tokens->currentToken());
|
||||
}
|
||||
}
|
||||
throw new CompileException('Unbalanced brackets.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats modifiers calling.
|
||||
* @param string|array $var
|
||||
* @throws CompileException
|
||||
*/
|
||||
public function modifierPass(MacroTokens $tokens, $var, bool $isContent = false): MacroTokens
|
||||
{
|
||||
$inside = false;
|
||||
$res = new MacroTokens($var);
|
||||
while ($tokens->nextToken()) {
|
||||
if ($tokens->isCurrent($tokens::T_WHITESPACE)) {
|
||||
$res->append(' ');
|
||||
|
||||
} elseif ($inside) {
|
||||
if ($tokens->isCurrent(':', ',')) {
|
||||
$res->append(', ');
|
||||
$tokens->nextAll($tokens::T_WHITESPACE);
|
||||
|
||||
} elseif ($tokens->isCurrent('|')) {
|
||||
$res->append(')');
|
||||
$inside = false;
|
||||
|
||||
} else {
|
||||
$res->append($tokens->currentToken());
|
||||
}
|
||||
} else {
|
||||
if ($tokens->isCurrent($tokens::T_SYMBOL)) {
|
||||
if ($tokens->isCurrent('escape')) {
|
||||
if ($isContent) {
|
||||
$res->prepend('LR\Filters::convertTo($_fi, ' . var_export(implode($this->context), true) . ', ')
|
||||
->append(')');
|
||||
} else {
|
||||
$res = $this->escapePass($res);
|
||||
}
|
||||
$tokens->nextToken('|');
|
||||
} elseif (!strcasecmp($tokens->currentValue(), 'checkurl')) {
|
||||
$res->prepend('LR\Filters::safeUrl(');
|
||||
$inside = true;
|
||||
} else {
|
||||
$name = strtolower($tokens->currentValue());
|
||||
$res->prepend($isContent
|
||||
? '$this->filters->filterContent(' . var_export($name, true) . ', $_fi, '
|
||||
: '($this->filters->' . $name . ')('
|
||||
);
|
||||
$inside = true;
|
||||
}
|
||||
} else {
|
||||
throw new CompileException("Modifier name must be alphanumeric string, '{$tokens->currentValue()}' given.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($inside) {
|
||||
$res->append(')');
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes expression in tokens.
|
||||
*/
|
||||
public function escapePass(MacroTokens $tokens): MacroTokens
|
||||
{
|
||||
$tokens = clone $tokens;
|
||||
[$contentType, $context] = $this->context;
|
||||
switch ($contentType) {
|
||||
case Compiler::CONTENT_XHTML:
|
||||
case Compiler::CONTENT_HTML:
|
||||
switch ($context) {
|
||||
case Compiler::CONTEXT_HTML_TEXT:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlText(')->append(')');
|
||||
case Compiler::CONTEXT_HTML_TAG:
|
||||
case Compiler::CONTEXT_HTML_ATTRIBUTE_UNQUOTED_URL:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlAttrUnquoted(')->append(')');
|
||||
case Compiler::CONTEXT_HTML_ATTRIBUTE:
|
||||
case Compiler::CONTEXT_HTML_ATTRIBUTE_URL:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlAttr(')->append(')');
|
||||
case Compiler::CONTEXT_HTML_ATTRIBUTE_JS:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlAttr(LR\Filters::escapeJs(')->append('))');
|
||||
case Compiler::CONTEXT_HTML_ATTRIBUTE_CSS:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlAttr(LR\Filters::escapeCss(')->append('))');
|
||||
case Compiler::CONTEXT_HTML_COMMENT:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlComment(')->append(')');
|
||||
case Compiler::CONTEXT_HTML_BOGUS_COMMENT:
|
||||
return $tokens->prepend('LR\Filters::escapeHtml(')->append(')');
|
||||
case Compiler::CONTEXT_HTML_JS:
|
||||
case Compiler::CONTEXT_HTML_CSS:
|
||||
return $tokens->prepend('LR\Filters::escape' . ucfirst($context) . '(')->append(')');
|
||||
default:
|
||||
throw new CompileException("Unknown context $contentType, $context.");
|
||||
}
|
||||
// break omitted
|
||||
case Compiler::CONTENT_XML:
|
||||
switch ($context) {
|
||||
case Compiler::CONTEXT_XML_TEXT:
|
||||
case Compiler::CONTEXT_XML_ATTRIBUTE:
|
||||
case Compiler::CONTEXT_XML_BOGUS_COMMENT:
|
||||
return $tokens->prepend('LR\Filters::escapeXml(')->append(')');
|
||||
case Compiler::CONTEXT_XML_COMMENT:
|
||||
return $tokens->prepend('LR\Filters::escapeHtmlComment(')->append(')');
|
||||
case Compiler::CONTEXT_XML_TAG:
|
||||
return $tokens->prepend('LR\Filters::escapeXmlAttrUnquoted(')->append(')');
|
||||
default:
|
||||
throw new CompileException("Unknown context $contentType, $context.");
|
||||
}
|
||||
// break omitted
|
||||
case Compiler::CONTENT_JS:
|
||||
case Compiler::CONTENT_CSS:
|
||||
case Compiler::CONTENT_ICAL:
|
||||
return $tokens->prepend('LR\Filters::escape' . ucfirst($contentType) . '(')->append(')');
|
||||
case Compiler::CONTENT_TEXT:
|
||||
return $tokens;
|
||||
case null:
|
||||
return $tokens->prepend('($this->filters->escape)(')->append(')');
|
||||
default:
|
||||
throw new CompileException("Unknown context $contentType.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Latte parser token.
|
||||
*/
|
||||
class Token
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public const
|
||||
TEXT = 'text',
|
||||
MACRO_TAG = 'macroTag', // latte macro tag
|
||||
HTML_TAG_BEGIN = 'htmlTagBegin', // begin of HTML tag or comment
|
||||
HTML_TAG_END = 'htmlTagEnd', // end of HTML tag or comment
|
||||
HTML_ATTRIBUTE_BEGIN = 'htmlAttributeBegin',
|
||||
HTML_ATTRIBUTE_END = 'htmlAttributeEnd',
|
||||
COMMENT = 'comment'; // latte comment
|
||||
|
||||
/** @var string token type [TEXT | MACRO_TAG | HTML_TAG_BEGIN | HTML_TAG_END | HTML_ATTRIBUTE_BEGIN | HTML_ATTRIBUTE_END | COMMENT] */
|
||||
public $type;
|
||||
|
||||
/** @var string original text content of the token */
|
||||
public $text;
|
||||
|
||||
/** @var int line number */
|
||||
public $line;
|
||||
|
||||
/** @var string name of macro tag, HTML tag or attribute; used for types MACRO_TAG, HTML_TAG_BEGIN, HTML_ATTRIBUTE_BEGIN */
|
||||
public $name;
|
||||
|
||||
/** @var string value of macro tag or HTML attribute; used for types MACRO_TAG, HTML_ATTRIBUTE_BEGIN */
|
||||
public $value;
|
||||
|
||||
/** @var string macro modifiers; used for type MACRO_TAG */
|
||||
public $modifiers;
|
||||
|
||||
/** @var bool is closing macro or HTML tag </tag>? used for types MACRO_TAG, HTML_TAG_BEGIN */
|
||||
public $closing;
|
||||
|
||||
/** @var bool is tag empty {name/}? used for type MACRO_TAG */
|
||||
public $empty;
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Traversing helper.
|
||||
* @internal
|
||||
*/
|
||||
class TokenIterator
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** @var array */
|
||||
public $tokens;
|
||||
|
||||
/** @var int */
|
||||
public $position = -1;
|
||||
|
||||
/** @var array */
|
||||
public $ignored = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param array[] $tokens
|
||||
*/
|
||||
public function __construct(array $tokens)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current token.
|
||||
*/
|
||||
public function currentToken(): ?array
|
||||
{
|
||||
return $this->tokens[$this->position] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current token value.
|
||||
*/
|
||||
public function currentValue(): ?string
|
||||
{
|
||||
return $this->tokens[$this->position][Tokenizer::VALUE] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns next token.
|
||||
* @param int|string $args desired token type or value
|
||||
*/
|
||||
public function nextToken(...$args): ?array
|
||||
{
|
||||
return $this->scan($args, true, true); // onlyFirst, advance
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns next token value.
|
||||
* @param int|string $args desired token type or value
|
||||
*/
|
||||
public function nextValue(...$args): ?string
|
||||
{
|
||||
return $this->scan($args, true, true, true); // onlyFirst, advance, strings
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all next tokens.
|
||||
* @param int|string $args desired token type or value
|
||||
* @return array[]
|
||||
*/
|
||||
public function nextAll(...$args): array
|
||||
{
|
||||
return $this->scan($args, false, true); // advance
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all next tokens until it sees a given token type or value.
|
||||
* @param int|string $args token type or value to stop before (required)
|
||||
* @return array[]
|
||||
*/
|
||||
public function nextUntil(...$args): array
|
||||
{
|
||||
return $this->scan($args, false, true, false, true); // advance, until
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns concatenation of all next token values.
|
||||
* @param int|string $args token type or value to be joined
|
||||
*/
|
||||
public function joinAll(...$args): string
|
||||
{
|
||||
return $this->scan($args, false, true, true); // advance, strings
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns concatenation of all next tokens until it sees a given token type or value.
|
||||
* @param int|string $args token type or value to stop before (required)
|
||||
*/
|
||||
public function joinUntil(...$args): string
|
||||
{
|
||||
return $this->scan($args, false, true, true, true); // advance, strings, until
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the current token.
|
||||
* @param int|string $args token type or value
|
||||
*/
|
||||
public function isCurrent(...$args): bool
|
||||
{
|
||||
if (!isset($this->tokens[$this->position])) {
|
||||
return false;
|
||||
}
|
||||
$token = $this->tokens[$this->position];
|
||||
return in_array($token[Tokenizer::VALUE], $args, true)
|
||||
|| in_array($token[Tokenizer::TYPE], $args, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the next token existence.
|
||||
* @param int|string $args token type or value
|
||||
*/
|
||||
public function isNext(...$args): bool
|
||||
{
|
||||
return (bool) $this->scan($args, true, false); // onlyFirst
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the previous token existence.
|
||||
* @param int|string $args token type or value
|
||||
*/
|
||||
public function isPrev(...$args): bool
|
||||
{
|
||||
return (bool) $this->scan($args, true, false, false, false, true); // onlyFirst, prev
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns next expected token or throws exception.
|
||||
* @param int|string $args desired token type or value
|
||||
* @throws CompileException
|
||||
*/
|
||||
public function consumeValue(...$args): string
|
||||
{
|
||||
if ($token = $this->scan($args, true, true)) { // onlyFirst, advance
|
||||
return $token[Tokenizer::VALUE];
|
||||
}
|
||||
$pos = $this->position + 1;
|
||||
while (($next = $this->tokens[$pos] ?? null) && in_array($next[Tokenizer::TYPE], $this->ignored, true)) {
|
||||
$pos++;
|
||||
}
|
||||
throw new CompileException($next ? "Unexpected token '" . $next[Tokenizer::VALUE] . "'." : 'Unexpected end.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->position = -1;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Moves cursor to next token.
|
||||
*/
|
||||
protected function next(): void
|
||||
{
|
||||
$this->position++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Looks for (first) (not) wanted tokens.
|
||||
* @param array $wanted of desired token types or values
|
||||
* @return mixed
|
||||
*/
|
||||
protected function scan(array $wanted, bool $onlyFirst, bool $advance, bool $strings = false, bool $until = false, bool $prev = false)
|
||||
{
|
||||
$res = $onlyFirst ? null : ($strings ? '' : []);
|
||||
$pos = $this->position + ($prev ? -1 : 1);
|
||||
do {
|
||||
if (!isset($this->tokens[$pos])) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
$token = $this->tokens[$pos];
|
||||
if (!$wanted || (in_array($token[Tokenizer::VALUE], $wanted, true) || in_array($token[Tokenizer::TYPE], $wanted, true)) ^ $until) {
|
||||
while ($advance && !$prev && $pos > $this->position) {
|
||||
$this->next();
|
||||
}
|
||||
|
||||
if ($onlyFirst) {
|
||||
return $strings ? $token[Tokenizer::VALUE] : $token;
|
||||
} elseif ($strings) {
|
||||
$res .= $token[Tokenizer::VALUE];
|
||||
} else {
|
||||
$res[] = $token;
|
||||
}
|
||||
|
||||
} elseif ($until || !in_array($token[Tokenizer::TYPE], $this->ignored, true)) {
|
||||
return $res;
|
||||
}
|
||||
$pos += $prev ? -1 : 1;
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Simple lexical analyser.
|
||||
* @internal
|
||||
*/
|
||||
class Tokenizer
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public const
|
||||
VALUE = 0,
|
||||
OFFSET = 1,
|
||||
TYPE = 2;
|
||||
|
||||
/** @var string */
|
||||
private $re;
|
||||
|
||||
/** @var array */
|
||||
private $types;
|
||||
|
||||
|
||||
/**
|
||||
* @param array $patterns of [(int) symbol type => pattern]
|
||||
* @param string $flags regular expression flag
|
||||
*/
|
||||
public function __construct(array $patterns, string $flags = '')
|
||||
{
|
||||
$this->re = '~(' . implode(')|(', $patterns) . ')~A' . $flags;
|
||||
$this->types = array_keys($patterns);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tokenizes string.
|
||||
*/
|
||||
public function tokenize(string $input): array
|
||||
{
|
||||
preg_match_all($this->re, $input, $tokens, PREG_SET_ORDER);
|
||||
if (preg_last_error()) {
|
||||
throw new RegexpException(null, preg_last_error());
|
||||
}
|
||||
$len = 0;
|
||||
$count = count($this->types);
|
||||
foreach ($tokens as &$match) {
|
||||
$type = null;
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
if (!isset($match[$i])) {
|
||||
break;
|
||||
} elseif ($match[$i] != null) {
|
||||
$type = $this->types[$i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$match = [self::VALUE => $match[0], self::OFFSET => $len, self::TYPE => $type];
|
||||
$len += strlen($match[self::VALUE]);
|
||||
}
|
||||
if ($len !== strlen($input)) {
|
||||
[$line, $col] = $this->getCoordinates($input, $len);
|
||||
$token = str_replace("\n", '\n', substr($input, $len, 10));
|
||||
throw new CompileException("Unexpected '$token' on line $line, column $col.");
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns position of token in input string.
|
||||
* @return array of [line, column]
|
||||
*/
|
||||
public static function getCoordinates(string $text, int $offset): array
|
||||
{
|
||||
$text = substr($text, 0, $offset);
|
||||
return [substr_count($text, "\n") + 1, $offset - strrpos("\n" . $text, "\n") + 1];
|
||||
}
|
||||
}
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Templating engine Latte.
|
||||
*/
|
||||
class Engine
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public const VERSION = '2.5.1';
|
||||
|
||||
/** Content types */
|
||||
public const
|
||||
CONTENT_HTML = 'html',
|
||||
CONTENT_XHTML = 'xhtml',
|
||||
CONTENT_XML = 'xml',
|
||||
CONTENT_JS = 'js',
|
||||
CONTENT_CSS = 'css',
|
||||
CONTENT_ICAL = 'ical',
|
||||
CONTENT_TEXT = 'text';
|
||||
|
||||
/** @var callable[] */
|
||||
public $onCompile = [];
|
||||
|
||||
/** @var Parser */
|
||||
private $parser;
|
||||
|
||||
/** @var Compiler */
|
||||
private $compiler;
|
||||
|
||||
/** @var ILoader */
|
||||
private $loader;
|
||||
|
||||
/** @var Runtime\FilterExecutor */
|
||||
private $filters;
|
||||
|
||||
/** @var array */
|
||||
private $providers = [];
|
||||
|
||||
/** @var string */
|
||||
private $contentType = self::CONTENT_HTML;
|
||||
|
||||
/** @var string */
|
||||
private $tempDirectory;
|
||||
|
||||
/** @var bool */
|
||||
private $autoRefresh = true;
|
||||
|
||||
/** @var bool */
|
||||
private $strictTypes = false;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->filters = new Runtime\FilterExecutor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders template to output.
|
||||
*/
|
||||
public function render(string $name, array $params = [], string $block = null): void
|
||||
{
|
||||
$this->createTemplate($name, $params + ['_renderblock' => $block])
|
||||
->render();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders template to string.
|
||||
*/
|
||||
public function renderToString(string $name, array $params = [], string $block = null): string
|
||||
{
|
||||
$template = $this->createTemplate($name, $params + ['_renderblock' => $block]);
|
||||
return $template->capture([$template, 'render']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates template object.
|
||||
*/
|
||||
public function createTemplate(string $name, array $params = []): Runtime\Template
|
||||
{
|
||||
$class = $this->getTemplateClass($name);
|
||||
if (!class_exists($class, false)) {
|
||||
$this->loadTemplate($name);
|
||||
}
|
||||
return new $class($this, $params, $this->filters, $this->providers, $name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compiles template to PHP code.
|
||||
*/
|
||||
public function compile(string $name): string
|
||||
{
|
||||
foreach ($this->onCompile ?: [] as $cb) {
|
||||
(Helpers::checkCallback($cb))($this);
|
||||
}
|
||||
$this->onCompile = [];
|
||||
|
||||
$source = $this->getLoader()->getContent($name);
|
||||
|
||||
try {
|
||||
$tokens = $this->getParser()->setContentType($this->contentType)
|
||||
->parse($source);
|
||||
|
||||
$code = $this->getCompiler()->setContentType($this->contentType)
|
||||
->compile($tokens, $this->getTemplateClass($name));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
if (!$e instanceof CompileException) {
|
||||
$e = new CompileException("Thrown exception '{$e->getMessage()}'", 0, $e);
|
||||
}
|
||||
$line = isset($tokens) ? $this->getCompiler()->getLine() : $this->getParser()->getLine();
|
||||
throw $e->setSource($source, $line, $name);
|
||||
}
|
||||
|
||||
if (!preg_match('#\n|\?#', $name)) {
|
||||
$code = "<?php\n// source: $name\n?>" . $code;
|
||||
}
|
||||
if ($this->strictTypes) {
|
||||
$code = "<?php\ndeclare(strict_types=1);\n?>" . $code;
|
||||
}
|
||||
$code = PhpHelpers::reformatCode($code);
|
||||
return $code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compiles template to cache.
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function warmupCache(string $name): void
|
||||
{
|
||||
if (!$this->tempDirectory) {
|
||||
throw new \LogicException('Path to temporary directory is not set.');
|
||||
}
|
||||
|
||||
$class = $this->getTemplateClass($name);
|
||||
if (!class_exists($class, false)) {
|
||||
$this->loadTemplate($name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function loadTemplate(string $name): void
|
||||
{
|
||||
if (!$this->tempDirectory) {
|
||||
$code = $this->compile($name);
|
||||
if (@eval('?>' . $code) === false) { // @ is escalated to exception
|
||||
throw (new CompileException('Error in template: ' . error_get_last()['message']))
|
||||
->setSource($code, error_get_last()['line'], "$name (compiled)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$file = $this->getCacheFile($name);
|
||||
|
||||
if (!$this->isExpired($file, $name) && (@include $file) !== false) { // @ - file may not exist
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_dir($this->tempDirectory) && !@mkdir($this->tempDirectory) && !is_dir($this->tempDirectory)) { // @ - dir may already exist
|
||||
throw new \RuntimeException("Unable to create directory '$this->tempDirectory'. " . error_get_last()['message']);
|
||||
}
|
||||
|
||||
$handle = @fopen("$file.lock", 'c+'); // @ is escalated to exception
|
||||
if (!$handle) {
|
||||
throw new \RuntimeException("Unable to create file '$file.lock'. " . error_get_last()['message']);
|
||||
} elseif (!@flock($handle, LOCK_EX)) { // @ is escalated to exception
|
||||
throw new \RuntimeException("Unable to acquire exclusive lock on '$file.lock'. " . error_get_last()['message']);
|
||||
}
|
||||
|
||||
if (!is_file($file) || $this->isExpired($file, $name)) {
|
||||
$code = $this->compile($name);
|
||||
if (file_put_contents("$file.tmp", $code) !== strlen($code) || !rename("$file.tmp", $file)) {
|
||||
@unlink("$file.tmp"); // @ - file may not exist
|
||||
throw new \RuntimeException("Unable to create '$file'.");
|
||||
} elseif (function_exists('opcache_invalidate')) {
|
||||
@opcache_invalidate($file, true); // @ can be restricted
|
||||
}
|
||||
}
|
||||
|
||||
if ((include $file) === false) {
|
||||
throw new \RuntimeException("Unable to load '$file'.");
|
||||
}
|
||||
|
||||
flock($handle, LOCK_UN);
|
||||
fclose($handle);
|
||||
@unlink("$file.lock"); // @ file may become locked on Windows
|
||||
}
|
||||
|
||||
|
||||
private function isExpired(string $file, string $name): bool
|
||||
{
|
||||
return $this->autoRefresh && $this->getLoader()->isExpired($name, (int) @filemtime($file)); // @ - file may not exist
|
||||
}
|
||||
|
||||
|
||||
public function getCacheFile(string $name): string
|
||||
{
|
||||
$hash = substr($this->getTemplateClass($name), 8);
|
||||
$base = preg_match('#([/\\\\][\w@.-]{3,35}){1,3}\z#', $name, $m)
|
||||
? preg_replace('#[^\w@.-]+#', '-', substr($m[0], 1)) . '--'
|
||||
: '';
|
||||
return "$this->tempDirectory/$base$hash.php";
|
||||
}
|
||||
|
||||
|
||||
public function getTemplateClass(string $name): string
|
||||
{
|
||||
$key = $this->getLoader()->getUniqueId($name) . "\00" . self::VERSION;
|
||||
return 'Template' . substr(md5($key), 0, 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers run-time filter.
|
||||
* @return static
|
||||
*/
|
||||
public function addFilter(?string $name, callable $callback)
|
||||
{
|
||||
$this->filters->add($name, $callback);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all run-time filters.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getFilters(): array
|
||||
{
|
||||
return $this->filters->getAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call a run-time filter.
|
||||
* @return mixed
|
||||
*/
|
||||
public function invokeFilter(string $name, array $args)
|
||||
{
|
||||
return ($this->filters->$name)(...$args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds new macro.
|
||||
* @return static
|
||||
*/
|
||||
public function addMacro(string $name, IMacro $macro)
|
||||
{
|
||||
$this->getCompiler()->addMacro($name, $macro);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds new provider.
|
||||
* @return static
|
||||
*/
|
||||
public function addProvider(string $name, $value)
|
||||
{
|
||||
$this->providers[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all providers.
|
||||
*/
|
||||
public function getProviders(): array
|
||||
{
|
||||
return $this->providers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function setContentType(string $type)
|
||||
{
|
||||
$this->contentType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets path to temporary directory.
|
||||
* @return static
|
||||
*/
|
||||
public function setTempDirectory(string $path)
|
||||
{
|
||||
$this->tempDirectory = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets auto-refresh mode.
|
||||
* @return static
|
||||
*/
|
||||
public function setAutoRefresh(bool $on = true)
|
||||
{
|
||||
$this->autoRefresh = $on;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables declare(strict_types=1) in templates.
|
||||
* @return static
|
||||
*/
|
||||
public function setStrictTypes(bool $on = true)
|
||||
{
|
||||
$this->strictTypes = $on;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getParser(): Parser
|
||||
{
|
||||
if (!$this->parser) {
|
||||
$this->parser = new Parser;
|
||||
}
|
||||
return $this->parser;
|
||||
}
|
||||
|
||||
|
||||
public function getCompiler(): Compiler
|
||||
{
|
||||
if (!$this->compiler) {
|
||||
$this->compiler = new Compiler;
|
||||
Macros\CoreMacros::install($this->compiler);
|
||||
Macros\BlockMacros::install($this->compiler);
|
||||
}
|
||||
return $this->compiler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function setLoader(ILoader $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getLoader(): ILoader
|
||||
{
|
||||
if (!$this->loader) {
|
||||
$this->loader = new Loaders\FileLoader;
|
||||
}
|
||||
return $this->loader;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Latte helpers.
|
||||
* @internal
|
||||
*/
|
||||
class Helpers
|
||||
{
|
||||
/** @var array empty (void) HTML elements */
|
||||
public static $emptyElements = [
|
||||
'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1, 'source' => 1, 'base' => 1,
|
||||
'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1, 'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1,
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Checks callback.
|
||||
*/
|
||||
public static function checkCallback($callable): callable
|
||||
{
|
||||
if (!is_callable($callable, false, $text)) {
|
||||
throw new \InvalidArgumentException("Callback '$text' is not callable.");
|
||||
}
|
||||
return $callable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the best suggestion.
|
||||
*/
|
||||
public static function getSuggestion(array $items, $value): ?string
|
||||
{
|
||||
$best = null;
|
||||
$min = (strlen($value) / 4 + 1) * 10 + .1;
|
||||
foreach (array_unique($items, SORT_REGULAR) as $item) {
|
||||
$item = is_object($item) ? $item->getName() : $item;
|
||||
if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
|
||||
$min = $len;
|
||||
$best = $item;
|
||||
}
|
||||
}
|
||||
return $best;
|
||||
}
|
||||
|
||||
|
||||
public static function removeFilter(string &$modifier, string $filter): bool
|
||||
{
|
||||
$modifier = preg_replace('#\|(' . $filter . ')\s?(?=\||\z)#i', '', $modifier, -1, $found);
|
||||
return (bool) $found;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the $haystack string with the prefix $needle?
|
||||
*/
|
||||
public static function startsWith(string $haystack, string $needle): bool
|
||||
{
|
||||
return strncmp($haystack, $needle, strlen($needle)) === 0;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Template loader.
|
||||
*/
|
||||
interface ILoader
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns template source code.
|
||||
*/
|
||||
function getContent($name);
|
||||
|
||||
/**
|
||||
* Checks whether template is expired.
|
||||
*/
|
||||
function isExpired($name, $time);
|
||||
|
||||
/**
|
||||
* Returns referred template name.
|
||||
*/
|
||||
function getReferredName($name, $referringName);
|
||||
|
||||
/**
|
||||
* Returns unique identifier for caching.
|
||||
*/
|
||||
function getUniqueId($name);
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Latte macro.
|
||||
*/
|
||||
interface IMacro
|
||||
{
|
||||
const
|
||||
AUTO_EMPTY = 4,
|
||||
AUTO_CLOSE = 64,
|
||||
ALLOWED_IN_HEAD = 128,
|
||||
DEFAULT_FLAGS = 0;
|
||||
|
||||
/**
|
||||
* Initializes before template parsing.
|
||||
* @return void
|
||||
*/
|
||||
function initialize();
|
||||
|
||||
/**
|
||||
* Finishes template parsing.
|
||||
* @return array|null [prolog, epilog]
|
||||
*/
|
||||
function finalize();
|
||||
|
||||
/**
|
||||
* New node is found. Returns false to reject.
|
||||
* @return bool|null
|
||||
*/
|
||||
function nodeOpened(MacroNode $node);
|
||||
|
||||
/**
|
||||
* Node is closed.
|
||||
* @return void
|
||||
*/
|
||||
function nodeClosed(MacroNode $node);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Loaders;
|
||||
|
||||
use Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Template loader.
|
||||
*/
|
||||
class FileLoader implements Latte\ILoader
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var string|null */
|
||||
private $baseDir;
|
||||
|
||||
|
||||
public function __construct($baseDir = null)
|
||||
{
|
||||
$this->baseDir = $baseDir ? $this->normalizePath("$baseDir/") : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns template source code.
|
||||
*/
|
||||
public function getContent($fileName): string
|
||||
{
|
||||
$file = $this->baseDir . $fileName;
|
||||
if ($this->baseDir && !Latte\Helpers::startsWith($this->normalizePath($file), $this->baseDir)) {
|
||||
throw new \RuntimeException("Template '$file' is not within the allowed path '$this->baseDir'.");
|
||||
|
||||
} elseif (!is_file($file)) {
|
||||
throw new \RuntimeException("Missing template file '$file'.");
|
||||
|
||||
} elseif ($this->isExpired($fileName, time())) {
|
||||
if (@touch($file) === false) {
|
||||
trigger_error("File's modification time is in the future. Cannot update it: " . error_get_last()['message'], E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
return file_get_contents($file);
|
||||
}
|
||||
|
||||
|
||||
public function isExpired($file, $time): bool
|
||||
{
|
||||
return @filemtime($this->baseDir . $file) > $time; // @ - stat may fail
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns referred template name.
|
||||
*/
|
||||
public function getReferredName($file, $referringFile): string
|
||||
{
|
||||
if ($this->baseDir || !preg_match('#/|\\\\|[a-z][a-z0-9+.-]*:#iA', $file)) {
|
||||
$file = $this->normalizePath($referringFile . '/../' . $file);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns unique identifier for caching.
|
||||
*/
|
||||
public function getUniqueId($file): string
|
||||
{
|
||||
return $this->baseDir . strtr($file, '/', DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
|
||||
private static function normalizePath(string $path): string
|
||||
{
|
||||
$res = [];
|
||||
foreach (explode('/', strtr($path, '\\', '/')) as $part) {
|
||||
if ($part === '..' && $res && end($res) !== '..') {
|
||||
array_pop($res);
|
||||
} elseif ($part !== '.') {
|
||||
$res[] = $part;
|
||||
}
|
||||
}
|
||||
return implode(DIRECTORY_SEPARATOR, $res);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Loaders;
|
||||
|
||||
use Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Template loader.
|
||||
*/
|
||||
class StringLoader implements Latte\ILoader
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var array|null [name => content] */
|
||||
private $templates;
|
||||
|
||||
|
||||
public function __construct(array $templates = null)
|
||||
{
|
||||
$this->templates = $templates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns template source code.
|
||||
*/
|
||||
public function getContent($name): string
|
||||
{
|
||||
if ($this->templates === null) {
|
||||
return $name;
|
||||
} elseif (isset($this->templates[$name])) {
|
||||
return $this->templates[$name];
|
||||
} else {
|
||||
throw new \RuntimeException("Missing template '$name'.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function isExpired($name, $time): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns referred template name.
|
||||
*/
|
||||
public function getReferredName($name, $referringName): string
|
||||
{
|
||||
if ($this->templates === null) {
|
||||
throw new \LogicException("Missing template '$name'.");
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns unique identifier for caching.
|
||||
*/
|
||||
public function getUniqueId($name): string
|
||||
{
|
||||
return $this->getContent($name);
|
||||
}
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Macros;
|
||||
|
||||
use Latte;
|
||||
use Latte\CompileException;
|
||||
use Latte\Helpers;
|
||||
use Latte\MacroNode;
|
||||
use Latte\PhpWriter;
|
||||
use Latte\Runtime\SnippetDriver;
|
||||
|
||||
|
||||
/**
|
||||
* Block macros.
|
||||
*/
|
||||
class BlockMacros extends MacroSet
|
||||
{
|
||||
/** @var array */
|
||||
private $namedBlocks = [];
|
||||
|
||||
/** @var array */
|
||||
private $blockTypes = [];
|
||||
|
||||
/** @var bool */
|
||||
private $extends;
|
||||
|
||||
/** @var string[] */
|
||||
private $imports;
|
||||
|
||||
|
||||
public static function install(Latte\Compiler $compiler): void
|
||||
{
|
||||
$me = new static($compiler);
|
||||
$me->addMacro('include', [$me, 'macroInclude']);
|
||||
$me->addMacro('includeblock', [$me, 'macroIncludeBlock']); // deprecated
|
||||
$me->addMacro('import', [$me, 'macroImport'], null, null, self::ALLOWED_IN_HEAD);
|
||||
$me->addMacro('extends', [$me, 'macroExtends'], null, null, self::ALLOWED_IN_HEAD);
|
||||
$me->addMacro('layout', [$me, 'macroExtends'], null, null, self::ALLOWED_IN_HEAD);
|
||||
$me->addMacro('snippet', [$me, 'macroBlock'], [$me, 'macroBlockEnd']);
|
||||
$me->addMacro('block', [$me, 'macroBlock'], [$me, 'macroBlockEnd'], null, self::AUTO_CLOSE);
|
||||
$me->addMacro('define', [$me, 'macroBlock'], [$me, 'macroBlockEnd']);
|
||||
$me->addMacro('snippetArea', [$me, 'macroBlock'], [$me, 'macroBlockEnd']);
|
||||
$me->addMacro('ifset', [$me, 'macroIfset'], '}');
|
||||
$me->addMacro('elseifset', [$me, 'macroIfset']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes before template parsing.
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$this->namedBlocks = [];
|
||||
$this->blockTypes = [];
|
||||
$this->extends = null;
|
||||
$this->imports = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finishes template parsing.
|
||||
*/
|
||||
public function finalize()
|
||||
{
|
||||
$compiler = $this->getCompiler();
|
||||
$functions = [];
|
||||
foreach ($this->namedBlocks as $name => $code) {
|
||||
$compiler->addMethod(
|
||||
$functions[$name] = $this->generateMethodName($name),
|
||||
'?>' . $compiler->expandTokens($code) . '<?php',
|
||||
'$_args'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->namedBlocks) {
|
||||
$compiler->addProperty('blocks', $functions);
|
||||
$compiler->addProperty('blockTypes', $this->blockTypes);
|
||||
}
|
||||
|
||||
return [
|
||||
($this->extends === null ? '' : '$this->parentName = ' . $this->extends . ';') . implode($this->imports),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/********************* macros ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* {include block}
|
||||
*/
|
||||
public function macroInclude(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$node->replaced = false;
|
||||
$destination = $node->tokenizer->fetchWord(); // destination [,] [params]
|
||||
if (!preg_match('~#|[\w-]+\z~A', $destination)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$destination = ltrim($destination, '#');
|
||||
$parent = $destination === 'parent';
|
||||
if ($destination === 'parent' || $destination === 'this') {
|
||||
for (
|
||||
$item = $node->parentNode;
|
||||
$item && $item->name !== 'block' && !isset($item->data->name);
|
||||
$item = $item->parentNode
|
||||
);
|
||||
if (!$item) {
|
||||
throw new CompileException("Cannot include $destination block outside of any block.");
|
||||
}
|
||||
$destination = $item->data->name;
|
||||
}
|
||||
|
||||
$noEscape = Helpers::removeFilter($node->modifiers, 'noescape');
|
||||
if (!$noEscape && Helpers::removeFilter($node->modifiers, 'escape')) {
|
||||
trigger_error('Macro ' . $node->getNotation() . ' provides auto-escaping, remove |escape.');
|
||||
}
|
||||
if ($node->modifiers && !$noEscape) {
|
||||
$node->modifiers .= '|escape';
|
||||
}
|
||||
return $writer->write(
|
||||
'$this->renderBlock' . ($parent ? 'Parent' : '') . '('
|
||||
. (strpos($destination, '$') === false ? var_export($destination, true) : $destination)
|
||||
. ', %node.array? + '
|
||||
. (isset($this->namedBlocks[$destination]) || $parent ? 'get_defined_vars()' : '$this->params')
|
||||
. ($node->modifiers
|
||||
? ', function ($s, $type) { $_fi = new LR\FilterInfo($type); return %modifyContent($s); }'
|
||||
: ($noEscape || $parent ? '' : ', ' . var_export(implode($node->context), true)))
|
||||
. ');'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {includeblock "file"}
|
||||
* @deprecated
|
||||
*/
|
||||
public function macroIncludeBlock(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
//trigger_error('Macro {includeblock} is deprecated, use similar macro {import}.', E_USER_DEPRECATED);
|
||||
$node->replaced = false;
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
return $writer->write(
|
||||
'ob_start(function () {}); $this->createTemplate(%node.word, %node.array? + get_defined_vars(), "includeblock")->renderToContentType(%var); echo rtrim(ob_get_clean());',
|
||||
implode($node->context)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {import "file"}
|
||||
*/
|
||||
public function macroImport(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
$destination = $node->tokenizer->fetchWord();
|
||||
$this->checkExtraArgs($node);
|
||||
$code = $writer->write('$this->createTemplate(%word, $this->params, "import")->render();', $destination);
|
||||
if ($this->getCompiler()->isInHead()) {
|
||||
$this->imports[] = $code;
|
||||
} else {
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {extends none | $var | "file"}
|
||||
*/
|
||||
public function macroExtends(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$notation = $node->getNotation();
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException("Modifiers are not allowed in $notation");
|
||||
} elseif (!$node->args) {
|
||||
throw new CompileException("Missing destination in $notation");
|
||||
} elseif ($node->parentNode) {
|
||||
throw new CompileException("$notation must be placed outside any macro.");
|
||||
} elseif ($this->extends !== null) {
|
||||
throw new CompileException("Multiple $notation declarations are not allowed.");
|
||||
} elseif ($node->args === 'none') {
|
||||
$this->extends = 'FALSE';
|
||||
} else {
|
||||
$this->extends = $writer->write('%node.word%node.args');
|
||||
}
|
||||
if (!$this->getCompiler()->isInHead()) {
|
||||
trigger_error("$notation must be placed in template head.", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {block [name]}
|
||||
* {snippet [name]}
|
||||
* {snippetArea [name]}
|
||||
* {define name}
|
||||
*/
|
||||
public function macroBlock(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$name = $node->tokenizer->fetchWord();
|
||||
|
||||
if ($node->name === 'block' && $name === null) { // anonymous block
|
||||
return $node->modifiers === '' ? '' : 'ob_start(function () {})';
|
||||
|
||||
} elseif ($node->name === 'define' && $node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
|
||||
$node->data->name = $name = ltrim((string) $name, '#');
|
||||
if ($name == null) {
|
||||
if ($node->name === 'define') {
|
||||
throw new CompileException('Missing block name.');
|
||||
}
|
||||
|
||||
} elseif (strpos($name, '$') !== false) { // dynamic block/snippet
|
||||
if ($node->name === 'snippet') {
|
||||
for (
|
||||
$parent = $node->parentNode;
|
||||
$parent && !($parent->name === 'snippet' || $parent->name === 'snippetArea');
|
||||
$parent = $parent->parentNode
|
||||
);
|
||||
if (!$parent) {
|
||||
throw new CompileException('Dynamic snippets are allowed only inside static snippet/snippetArea.');
|
||||
}
|
||||
$parent->data->dynamic = true;
|
||||
$node->data->leave = true;
|
||||
$node->closingCode = '<?php $this->global->snippetDriver->leave(); ?>';
|
||||
$enterCode = '$this->global->snippetDriver->enter(' . $writer->formatWord($name) . ', "' . SnippetDriver::TYPE_DYNAMIC . '");';
|
||||
|
||||
if ($node->prefix) {
|
||||
$node->attrCode = $writer->write("<?php echo ' id=\"' . htmlSpecialChars(\$this->global->snippetDriver->getHtmlId({$writer->formatWord($name)})) . '\"' ?>");
|
||||
return $writer->write($enterCode);
|
||||
}
|
||||
$node->closingCode .= "\n</div>";
|
||||
$this->checkExtraArgs($node);
|
||||
return $writer->write("?>\n<div id=\"<?php echo htmlSpecialChars(\$this->global->snippetDriver->getHtmlId({$writer->formatWord($name)})) ?>\"><?php " . $enterCode);
|
||||
|
||||
} else {
|
||||
$node->data->leave = true;
|
||||
$node->data->func = $this->generateMethodName($name);
|
||||
$fname = $writer->formatWord($name);
|
||||
if ($node->name === 'define') {
|
||||
$node->closingCode = '<?php ?>';
|
||||
} else {
|
||||
if (Helpers::startsWith((string) $node->context[1], Latte\Compiler::CONTEXT_HTML_ATTRIBUTE)) {
|
||||
$node->context[1] = '';
|
||||
$node->modifiers .= '|escape';
|
||||
} elseif ($node->modifiers) {
|
||||
$node->modifiers .= '|escape';
|
||||
}
|
||||
$node->closingCode = $writer->write('<?php $this->renderBlock(%raw, get_defined_vars()'
|
||||
. ($node->modifiers ? ', function ($s, $type) { $_fi = new LR\FilterInfo($type); return %modifyContent($s); }' : '') . '); ?>', $fname);
|
||||
}
|
||||
$blockType = var_export(implode($node->context), true);
|
||||
$this->checkExtraArgs($node);
|
||||
return "\$this->checkBlockContentType($blockType, $fname);"
|
||||
. "\$this->blockQueue[$fname][] = [\$this, '{$node->data->func}'];";
|
||||
}
|
||||
}
|
||||
|
||||
// static snippet/snippetArea
|
||||
if ($node->name === 'snippet' || $node->name === 'snippetArea') {
|
||||
if ($node->prefix && isset($node->htmlNode->attrs['id'])) {
|
||||
throw new CompileException('Cannot combine HTML attribute id with n:snippet.');
|
||||
}
|
||||
$node->data->name = $name = '_' . $name;
|
||||
}
|
||||
|
||||
if (isset($this->namedBlocks[$name])) {
|
||||
throw new CompileException("Cannot redeclare static {$node->name} '$name'");
|
||||
}
|
||||
$extendsCheck = $this->namedBlocks ? '' : 'if ($this->getParentName()) return get_defined_vars();';
|
||||
$this->namedBlocks[$name] = true;
|
||||
|
||||
if (Helpers::removeFilter($node->modifiers, 'escape')) {
|
||||
trigger_error('Macro ' . $node->getNotation() . ' provides auto-escaping, remove |escape.');
|
||||
}
|
||||
if (Helpers::startsWith((string) $node->context[1], Latte\Compiler::CONTEXT_HTML_ATTRIBUTE)) {
|
||||
$node->context[1] = '';
|
||||
$node->modifiers .= '|escape';
|
||||
} elseif ($node->modifiers) {
|
||||
$node->modifiers .= '|escape';
|
||||
}
|
||||
$this->blockTypes[$name] = implode($node->context);
|
||||
|
||||
$include = '$this->renderBlock(%var, ' . (($node->name === 'snippet' || $node->name === 'snippetArea') ? '$this->params' : 'get_defined_vars()')
|
||||
. ($node->modifiers ? ', function ($s, $type) { $_fi = new LR\FilterInfo($type); return %modifyContent($s); }' : '') . ')';
|
||||
|
||||
if ($node->name === 'snippet') {
|
||||
if ($node->prefix) {
|
||||
if (isset($node->htmlNode->macroAttrs['foreach'])) {
|
||||
trigger_error('Combination of n:snippet with n:foreach is invalid, use n:inner-foreach.', E_USER_WARNING);
|
||||
}
|
||||
$node->attrCode = $writer->write('<?php echo \' id="\' . htmlSpecialChars($this->global->snippetDriver->getHtmlId(%var)) . \'"\' ?>', (string) substr($name, 1));
|
||||
return $writer->write($include, $name);
|
||||
}
|
||||
$this->checkExtraArgs($node);
|
||||
return $writer->write("?>\n<div id=\"<?php echo htmlSpecialChars(\$this->global->snippetDriver->getHtmlId(%var)) ?>\"><?php $include ?>\n</div><?php ",
|
||||
(string) substr($name, 1), $name
|
||||
);
|
||||
|
||||
} elseif ($node->name === 'define') {
|
||||
$tokens = $node->tokenizer;
|
||||
$args = [];
|
||||
while ($tokens->isNext()) {
|
||||
$args[] = $tokens->consumeValue($tokens::T_VARIABLE);
|
||||
if ($tokens->isNext()) {
|
||||
$tokens->consumeValue(',');
|
||||
}
|
||||
}
|
||||
if ($args) {
|
||||
$node->data->args = 'list(' . implode(', ', $args) . ') = $_args + [' . str_repeat('NULL, ', count($args)) . '];';
|
||||
}
|
||||
return $extendsCheck;
|
||||
|
||||
} else { // block, snippetArea
|
||||
$this->checkExtraArgs($node);
|
||||
return $writer->write($extendsCheck . $include, $name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {/block}
|
||||
* {/snippet}
|
||||
* {/snippetArea}
|
||||
* {/define}
|
||||
*/
|
||||
public function macroBlockEnd(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if (isset($node->data->name)) { // block, snippet, define
|
||||
if ($asInner = $node->name === 'snippet' && $node->prefix === MacroNode::PREFIX_NONE) {
|
||||
$node->content = $node->innerContent;
|
||||
}
|
||||
|
||||
if (($node->name === 'snippet' || $node->name === 'snippetArea') && strpos($node->data->name, '$') === false) {
|
||||
$type = $node->name === 'snippet' ? SnippetDriver::TYPE_STATIC : SnippetDriver::TYPE_AREA;
|
||||
$node->content = '<?php $this->global->snippetDriver->enter('
|
||||
. $writer->formatWord(substr($node->data->name, 1))
|
||||
. ', "' . $type . '"); ?>'
|
||||
. preg_replace('#(?<=\n)[ \t]+\z#', '', $node->content) . '<?php $this->global->snippetDriver->leave(); ?>';
|
||||
}
|
||||
if (empty($node->data->leave)) {
|
||||
if (preg_match('#\$|n:#', $node->content)) {
|
||||
$node->content = '<?php ' . (isset($node->data->args) ? 'extract($this->params); ' . $node->data->args : 'extract($_args);') . ' ?>'
|
||||
. $node->content;
|
||||
}
|
||||
$this->namedBlocks[$node->data->name] = $tmp = preg_replace('#^\n+|(?<=\n)[ \t]+\z#', '', $node->content);
|
||||
$node->content = substr_replace($node->content, $node->openingCode . "\n", strspn($node->content, "\n"), strlen($tmp));
|
||||
$node->openingCode = '<?php ?>';
|
||||
|
||||
} elseif (isset($node->data->func)) {
|
||||
$node->content = rtrim($node->content, " \t");
|
||||
$this->getCompiler()->addMethod(
|
||||
$node->data->func,
|
||||
$this->getCompiler()->expandTokens("extract(\$_args);\n?>$node->content<?php"),
|
||||
'$_args'
|
||||
);
|
||||
$node->content = '';
|
||||
}
|
||||
|
||||
if ($asInner) { // n:snippet -> n:inner-snippet
|
||||
$node->innerContent = $node->openingCode . $node->content . $node->closingCode;
|
||||
$node->closingCode = $node->openingCode = '<?php ?>';
|
||||
}
|
||||
return ' '; // consume next new line
|
||||
|
||||
} elseif ($node->modifiers) { // anonymous block with modifier
|
||||
$node->modifiers .= '|escape';
|
||||
return $writer->write('$_fi = new LR\FilterInfo(%var); echo %modifyContent(ob_get_clean());', $node->context[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {ifset block}
|
||||
* {elseifset block}
|
||||
*/
|
||||
public function macroIfset(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
if (!preg_match('~#|[\w-]+\z~A', $node->args)) {
|
||||
return false;
|
||||
}
|
||||
$list = [];
|
||||
while (($name = $node->tokenizer->fetchWord()) !== null) {
|
||||
$list[] = preg_match('~#|[\w-]+\z~A', $name)
|
||||
? '$this->blockQueue["' . ltrim($name, '#') . '"]'
|
||||
: $writer->formatArgs(new Latte\MacroTokens($name));
|
||||
}
|
||||
return ($node->name === 'elseifset' ? '} else' : '')
|
||||
. 'if (isset(' . implode(', ', $list) . ')) {';
|
||||
}
|
||||
|
||||
|
||||
private function generateMethodName(string $blockName): string
|
||||
{
|
||||
$clean = trim(preg_replace('#\W+#', '_', $blockName), '_');
|
||||
$name = 'block' . ucfirst($clean);
|
||||
$methods = array_keys($this->getCompiler()->getMethods());
|
||||
if (!$clean || in_array(strtolower($name), array_map('strtolower', $methods), true)) {
|
||||
$name .= '_' . substr(md5($blockName), 0, 5);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
+515
@@ -0,0 +1,515 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Macros;
|
||||
|
||||
use Latte;
|
||||
use Latte\CompileException;
|
||||
use Latte\Engine;
|
||||
use Latte\Helpers;
|
||||
use Latte\MacroNode;
|
||||
use Latte\PhpWriter;
|
||||
|
||||
|
||||
/**
|
||||
* Basic macros for Latte.
|
||||
*
|
||||
* - {if ?} ... {elseif ?} ... {else} ... {/if}
|
||||
* - {ifset ?} ... {elseifset ?} ... {/ifset}
|
||||
* - {for ?} ... {/for}
|
||||
* - {foreach ?} ... {/foreach}
|
||||
* - {$variable} with escaping
|
||||
* - {=expression} echo with escaping
|
||||
* - {php expression} evaluate PHP statement
|
||||
* - {_expression} echo translation with escaping
|
||||
* - {capture ?} ... {/capture} capture block to parameter
|
||||
* - {spaceless} ... {/spaceless} compress whitespaces
|
||||
* - {var var => value} set template parameter
|
||||
* - {default var => value} set default template parameter
|
||||
* - {dump $var}
|
||||
* - {debugbreak}
|
||||
* - {contentType ...} HTTP Content-Type header
|
||||
* - {l} {r} to display { }
|
||||
*/
|
||||
class CoreMacros extends MacroSet
|
||||
{
|
||||
/** @var array */
|
||||
private $overwrittenVars;
|
||||
|
||||
|
||||
public static function install(Latte\Compiler $compiler): void
|
||||
{
|
||||
$me = new static($compiler);
|
||||
|
||||
$me->addMacro('if', [$me, 'macroIf'], [$me, 'macroEndIf']);
|
||||
$me->addMacro('elseif', '} elseif (%node.args) {');
|
||||
$me->addMacro('else', [$me, 'macroElse']);
|
||||
$me->addMacro('ifset', 'if (isset(%node.args)) {', '}');
|
||||
$me->addMacro('elseifset', '} elseif (isset(%node.args)) {');
|
||||
$me->addMacro('ifcontent', [$me, 'macroIfContent'], [$me, 'macroEndIfContent']);
|
||||
|
||||
$me->addMacro('switch', '$this->global->switch[] = (%node.args); if (false) {', '} array_pop($this->global->switch)');
|
||||
$me->addMacro('case', '} elseif (end($this->global->switch) === (%node.args)) {');
|
||||
|
||||
$me->addMacro('foreach', '', [$me, 'macroEndForeach']);
|
||||
$me->addMacro('for', 'for (%node.args) {', '}');
|
||||
$me->addMacro('while', [$me, 'macroWhile'], [$me, 'macroEndWhile']);
|
||||
$me->addMacro('continueIf', [$me, 'macroBreakContinueIf']);
|
||||
$me->addMacro('breakIf', [$me, 'macroBreakContinueIf']);
|
||||
$me->addMacro('first', 'if ($iterator->isFirst(%node.args)) {', '}');
|
||||
$me->addMacro('last', 'if ($iterator->isLast(%node.args)) {', '}');
|
||||
$me->addMacro('sep', 'if (!$iterator->isLast(%node.args)) {', '}');
|
||||
|
||||
$me->addMacro('var', [$me, 'macroVar']);
|
||||
$me->addMacro('default', [$me, 'macroVar']);
|
||||
$me->addMacro('dump', [$me, 'macroDump']);
|
||||
$me->addMacro('debugbreak', [$me, 'macroDebugbreak']);
|
||||
$me->addMacro('l', '?>{<?php');
|
||||
$me->addMacro('r', '?>}<?php');
|
||||
|
||||
$me->addMacro('_', [$me, 'macroTranslate'], [$me, 'macroTranslate']);
|
||||
$me->addMacro('=', [$me, 'macroExpr']);
|
||||
|
||||
$me->addMacro('capture', [$me, 'macroCapture'], [$me, 'macroCaptureEnd']);
|
||||
$me->addMacro('spaceless', [$me, 'macroSpaceless'], [$me, 'macroSpaceless']);
|
||||
$me->addMacro('include', [$me, 'macroInclude']);
|
||||
$me->addMacro('contentType', [$me, 'macroContentType'], null, null, self::ALLOWED_IN_HEAD);
|
||||
$me->addMacro('php', [$me, 'macroExpr']);
|
||||
|
||||
$me->addMacro('class', null, null, [$me, 'macroClass']);
|
||||
$me->addMacro('attr', null, null, [$me, 'macroAttr']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes before template parsing.
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$this->overwrittenVars = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finishes template parsing.
|
||||
* @return array|null [prolog, epilog]
|
||||
*/
|
||||
public function finalize()
|
||||
{
|
||||
$code = '';
|
||||
foreach ($this->overwrittenVars as $var => $lines) {
|
||||
$s = var_export($var, true);
|
||||
$code .= 'if (isset($this->params[' . var_export($var, true)
|
||||
. "])) trigger_error('Variable $" . addcslashes($var, "'") . ' overwritten in foreach on line ' . implode(', ', $lines) . "'); ";
|
||||
}
|
||||
$code = $code ? 'if (!$this->getReferringTemplate() || $this->getReferenceType() === "extends") { ' . $code . '}' : '';
|
||||
return [$code];
|
||||
}
|
||||
|
||||
|
||||
/********************* macros ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* {if ...}
|
||||
*/
|
||||
public function macroIf(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
if ($node->data->capture = ($node->args === '')) {
|
||||
return 'ob_start(function () {})';
|
||||
}
|
||||
if ($node->prefix === $node::PREFIX_TAG) {
|
||||
return $writer->write($node->htmlNode->closing ? 'if (array_pop($this->global->ifs)) {' : 'if ($this->global->ifs[] = (%node.args)) {');
|
||||
}
|
||||
return $writer->write('if (%node.args) {');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {/if ...}
|
||||
*/
|
||||
public function macroEndIf(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->data->capture) {
|
||||
if ($node->args === '') {
|
||||
throw new CompileException('Missing condition in {if} macro.');
|
||||
}
|
||||
return $writer->write('if (%node.args) '
|
||||
. (isset($node->data->else) ? '{ ob_end_clean(); echo ob_get_clean(); }' : 'echo ob_get_clean();')
|
||||
. ' else '
|
||||
. (isset($node->data->else) ? '{ $this->global->else = ob_get_clean(); ob_end_clean(); echo $this->global->else; }' : 'ob_end_clean();')
|
||||
);
|
||||
}
|
||||
return '}';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {else}
|
||||
*/
|
||||
public function macroElse(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
} elseif ($node->args) {
|
||||
$hint = Helpers::startsWith($node->args, 'if') ? ', did you mean {elseif}?' : '';
|
||||
throw new CompileException('Arguments are not allowed in ' . $node->getNotation() . $hint);
|
||||
}
|
||||
$ifNode = $node->parentNode;
|
||||
if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) {
|
||||
if (isset($ifNode->data->else)) {
|
||||
throw new CompileException('Macro {if} supports only one {else}.');
|
||||
}
|
||||
$ifNode->data->else = true;
|
||||
return 'ob_start(function () {})';
|
||||
}
|
||||
return '} else {';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* n:ifcontent
|
||||
*/
|
||||
public function macroIfContent(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if (!$node->prefix || $node->prefix !== MacroNode::PREFIX_NONE) {
|
||||
throw new CompileException('Unknown ' . $node->getNotation() . ", use n:{$node->name} attribute.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* n:ifcontent
|
||||
*/
|
||||
public function macroEndIfContent(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$node->openingCode = '<?php ob_start(function () {}); ?>';
|
||||
$node->innerContent = '<?php ob_start(); ?>' . $node->innerContent . '<?php $this->global->ifcontent = ob_get_flush(); ?>';
|
||||
$node->closingCode = '<?php if (rtrim($this->global->ifcontent) === "") ob_end_clean(); else echo ob_get_clean(); ?>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {_$var |modifiers}
|
||||
*/
|
||||
public function macroTranslate(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->closing) {
|
||||
if (strpos($node->content, '<?php') === false) {
|
||||
$value = var_export($node->content, true);
|
||||
$node->content = '';
|
||||
} else {
|
||||
$node->openingCode = '<?php ob_start(function () {}) ?>' . $node->openingCode;
|
||||
$value = 'ob_get_clean()';
|
||||
}
|
||||
|
||||
return $writer->write('$_fi = new LR\FilterInfo(%var); echo %modifyContent($this->filters->filterContent("translate", $_fi, %raw))', $node->context[0], $value);
|
||||
|
||||
} elseif ($node->empty = ($node->args !== '')) {
|
||||
return $writer->write('echo %modify(($this->filters->translate)(%node.args))');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {include "file" [,] [params]}
|
||||
*/
|
||||
public function macroInclude(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$node->replaced = false;
|
||||
$noEscape = Helpers::removeFilter($node->modifiers, 'noescape');
|
||||
if (!$noEscape && Helpers::removeFilter($node->modifiers, 'escape')) {
|
||||
trigger_error('Macro {include} provides auto-escaping, remove |escape.');
|
||||
}
|
||||
if ($node->modifiers && !$noEscape) {
|
||||
$node->modifiers .= '|escape';
|
||||
}
|
||||
return $writer->write(
|
||||
'/* line ' . $node->startLine . ' */
|
||||
$this->createTemplate(%node.word, %node.array? + $this->params, "include")->renderToContentType(%raw);',
|
||||
$node->modifiers
|
||||
? $writer->write('function ($s, $type) { $_fi = new LR\FilterInfo($type); return %modifyContent($s); }')
|
||||
: var_export($noEscape ? null : implode($node->context), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {capture $variable}
|
||||
*/
|
||||
public function macroCapture(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$variable = $node->tokenizer->fetchWord();
|
||||
if (!Helpers::startsWith($variable, '$')) {
|
||||
throw new CompileException("Invalid capture block variable '$variable'");
|
||||
}
|
||||
$this->checkExtraArgs($node);
|
||||
$node->data->variable = $variable;
|
||||
return 'ob_start(function () {})';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {/capture}
|
||||
*/
|
||||
public function macroCaptureEnd(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$body = in_array($node->context[0], [Engine::CONTENT_HTML, Engine::CONTENT_XHTML], true)
|
||||
? 'ob_get_length() ? new LR\\Html(ob_get_clean()) : ob_get_clean()'
|
||||
: 'ob_get_clean()';
|
||||
return $writer->write("\$_fi = new LR\\FilterInfo(%var); %raw = %modifyContent($body);", $node->context[0], $node->data->variable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {spaceless} ... {/spaceless}
|
||||
*/
|
||||
public function macroSpaceless(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers || $node->args) {
|
||||
throw new CompileException('Modifiers and arguments are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
$node->openingCode = in_array($node->context[0], [Engine::CONTENT_HTML, Engine::CONTENT_XHTML], true)
|
||||
? '<?php ob_start(function ($s, $phase) { static $strip = true; return LR\Filters::spacelessHtml($s, $phase, $strip); }, 4096); ?>'
|
||||
: "<?php ob_start('Latte\\Runtime\\Filters::spacelessText', 4096); ?>";
|
||||
$node->closingCode = '<?php ob_end_flush(); ?>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {while ...}
|
||||
*/
|
||||
public function macroWhile(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
if ($node->data->do = ($node->args === '')) {
|
||||
return 'do {';
|
||||
}
|
||||
return $writer->write('while (%node.args) {');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {/while ...}
|
||||
*/
|
||||
public function macroEndWhile(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->data->do) {
|
||||
if ($node->args === '') {
|
||||
throw new CompileException('Missing condition in {while} macro.');
|
||||
}
|
||||
return $writer->write('} while (%node.args);');
|
||||
}
|
||||
return '}';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {foreach ...}
|
||||
*/
|
||||
public function macroEndForeach(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
$noCheck = Helpers::removeFilter($node->modifiers, 'nocheck');
|
||||
$noIterator = Helpers::removeFilter($node->modifiers, 'noiterator');
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Only modifiers |noiterator and |nocheck are allowed here.');
|
||||
}
|
||||
$node->openingCode = '<?php $iterations = 0; ';
|
||||
$args = $writer->formatArgs();
|
||||
if (!$noCheck) {
|
||||
preg_match('#.+\s+as\s*\$(\w+)(?:\s*=>\s*\$(\w+))?#i', $args, $m);
|
||||
for ($i = 1; $i < count($m); $i++) {
|
||||
$this->overwrittenVars[$m[$i]][] = $node->startLine;
|
||||
}
|
||||
}
|
||||
if (!$noIterator && preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) {
|
||||
$node->openingCode .= 'foreach ($iterator = $this->global->its[] = new LR\CachingIterator('
|
||||
. preg_replace('#(.*)\s+as\s+#i', '$1) as ', $args, 1) . ') { ?>';
|
||||
$node->closingCode = '<?php $iterations++; } array_pop($this->global->its); $iterator = end($this->global->its); ?>';
|
||||
} else {
|
||||
$node->openingCode .= 'foreach (' . $args . ') { ?>';
|
||||
$node->closingCode = '<?php $iterations++; } ?>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {breakIf ...}
|
||||
* {continueIf ...}
|
||||
*/
|
||||
public function macroBreakContinueIf(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
$cmd = str_replace('If', '', $node->name);
|
||||
if ($node->parentNode && $node->parentNode->prefix === $node::PREFIX_NONE) {
|
||||
return $writer->write("if (%node.args) { echo \"</{$node->parentNode->htmlNode->name}>\\n\"; $cmd; }");
|
||||
}
|
||||
return $writer->write("if (%node.args) $cmd;");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* n:class="..."
|
||||
*/
|
||||
public function macroClass(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if (isset($node->htmlNode->attrs['class'])) {
|
||||
throw new CompileException('It is not possible to combine class with n:class.');
|
||||
}
|
||||
return $writer->write('if ($_tmp = array_filter(%node.array)) echo \' class="\', %escape(implode(" ", array_unique($_tmp))), \'"\'');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* n:attr="..."
|
||||
*/
|
||||
public function macroAttr(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
return $writer->write('$_tmp = %node.array; echo LR\Filters::htmlAttributes(isset($_tmp[0]) && is_array($_tmp[0]) ? $_tmp[0] : $_tmp);');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {dump ...}
|
||||
*/
|
||||
public function macroDump(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
$args = $writer->formatArgs();
|
||||
return $writer->write(
|
||||
'Tracy\Debugger::barDump(' . ($args ? "($args)" : 'get_defined_vars()') . ', %var);',
|
||||
$args ?: 'variables'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {debugbreak ...}
|
||||
*/
|
||||
public function macroDebugbreak(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
if (function_exists($func = 'debugbreak') || function_exists($func = 'xdebug_break')) {
|
||||
return $writer->write($node->args == null ? "$func()" : "if (%node.args) $func();");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {var ...}
|
||||
* {default ...}
|
||||
*/
|
||||
public function macroVar(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
if ($node->args === '' && $node->parentNode && $node->parentNode->name === 'switch') {
|
||||
return '} else {';
|
||||
}
|
||||
|
||||
$var = true;
|
||||
$tokens = $writer->preprocess();
|
||||
$res = new Latte\MacroTokens;
|
||||
while ($tokens->nextToken()) {
|
||||
if ($var && $tokens->isCurrent($tokens::T_SYMBOL, $tokens::T_VARIABLE)) {
|
||||
if ($node->name === 'default') {
|
||||
$res->append("'" . ltrim($tokens->currentValue(), '$') . "'");
|
||||
} else {
|
||||
$res->append('$' . ltrim($tokens->currentValue(), '$'));
|
||||
}
|
||||
$var = null;
|
||||
|
||||
} elseif ($tokens->isCurrent('=', '=>') && $tokens->depth === 0) {
|
||||
$res->append($node->name === 'default' ? '=>' : '=');
|
||||
$var = false;
|
||||
|
||||
} elseif ($tokens->isCurrent(',') && $tokens->depth === 0) {
|
||||
if ($var === null) {
|
||||
$res->append($node->name === 'default' ? '=>null' : '=null');
|
||||
}
|
||||
$res->append($node->name === 'default' ? ',' : ';');
|
||||
$var = true;
|
||||
|
||||
} elseif ($var === null && $node->name === 'default' && !$tokens->isCurrent($tokens::T_WHITESPACE)) {
|
||||
throw new CompileException("Unexpected '{$tokens->currentValue()}' in {default $node->args}");
|
||||
|
||||
} else {
|
||||
$res->append($tokens->currentToken());
|
||||
}
|
||||
}
|
||||
if ($var === null) {
|
||||
$res->append($node->name === 'default' ? '=>null' : '=null');
|
||||
}
|
||||
$out = $writer->quotingPass($res)->joinAll();
|
||||
return $node->name === 'default' ? "extract([$out], EXTR_SKIP)" : "$out;";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {= ...}
|
||||
* {php ...}
|
||||
*/
|
||||
public function macroExpr(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
return $writer->write($node->name === '='
|
||||
? "echo %modify(%node.args) /* line $node->startLine */"
|
||||
: '%modify(%node.args);'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {contentType ...}
|
||||
*/
|
||||
public function macroContentType(MacroNode $node, PhpWriter $writer)
|
||||
{
|
||||
if (
|
||||
!$this->getCompiler()->isInHead()
|
||||
&& !($node->htmlNode && strtolower($node->htmlNode->name) === 'script' && strpos($node->args, 'html') !== false)
|
||||
) {
|
||||
throw new CompileException($node->getNotation() . ' is allowed only in template header.');
|
||||
}
|
||||
$compiler = $this->getCompiler();
|
||||
if ($node->modifiers) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
} elseif (strpos($node->args, 'xhtml') !== false) {
|
||||
$type = $compiler::CONTENT_XHTML;
|
||||
} elseif (strpos($node->args, 'html') !== false) {
|
||||
$type = $compiler::CONTENT_HTML;
|
||||
} elseif (strpos($node->args, 'xml') !== false) {
|
||||
$type = $compiler::CONTENT_XML;
|
||||
} elseif (strpos($node->args, 'javascript') !== false) {
|
||||
$type = $compiler::CONTENT_JS;
|
||||
} elseif (strpos($node->args, 'css') !== false) {
|
||||
$type = $compiler::CONTENT_CSS;
|
||||
} elseif (strpos($node->args, 'calendar') !== false) {
|
||||
$type = $compiler::CONTENT_ICAL;
|
||||
} else {
|
||||
$type = $compiler::CONTENT_TEXT;
|
||||
}
|
||||
$compiler->setContentType($type);
|
||||
|
||||
if (strpos($node->args, '/') && !$node->htmlNode) {
|
||||
return $writer->write('if (empty($this->global->coreCaptured) && in_array($this->getReferenceType(), ["extends", null], true)) header(%var);', "Content-Type: $node->args");
|
||||
}
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Macros;
|
||||
|
||||
use Latte;
|
||||
use Latte\CompileException;
|
||||
use Latte\MacroNode;
|
||||
|
||||
|
||||
/**
|
||||
* Base IMacro implementation. Allows add multiple macros.
|
||||
*/
|
||||
class MacroSet implements Latte\IMacro
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var Latte\Compiler */
|
||||
private $compiler;
|
||||
|
||||
/** @var array */
|
||||
private $macros;
|
||||
|
||||
|
||||
public function __construct(Latte\Compiler $compiler)
|
||||
{
|
||||
$this->compiler = $compiler;
|
||||
}
|
||||
|
||||
|
||||
public function addMacro(string $name, $begin, $end = null, $attr = null, int $flags = null)
|
||||
{
|
||||
if (!$begin && !$end && !$attr) {
|
||||
throw new \InvalidArgumentException("At least one argument must be specified for macro '$name'.");
|
||||
}
|
||||
foreach ([$begin, $end, $attr] as $arg) {
|
||||
if ($arg && !is_string($arg)) {
|
||||
Latte\Helpers::checkCallback($arg);
|
||||
}
|
||||
}
|
||||
|
||||
$this->macros[$name] = [$begin, $end, $attr];
|
||||
$this->compiler->addMacro($name, $this, $flags);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes before template parsing.
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finishes template parsing.
|
||||
* @return array|null [prolog, epilog]
|
||||
*/
|
||||
public function finalize()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New node is found.
|
||||
* @return bool|null
|
||||
*/
|
||||
public function nodeOpened(MacroNode $node)
|
||||
{
|
||||
[$begin, $end, $attr] = $this->macros[$node->name];
|
||||
$node->empty = !$end;
|
||||
|
||||
if (
|
||||
$node->modifiers
|
||||
&& (!$begin || (is_string($begin) && strpos($begin, '%modify') === false))
|
||||
&& (!$end || (is_string($end) && strpos($end, '%modify') === false))
|
||||
&& (!$attr || (is_string($attr) && strpos($attr, '%modify') === false))
|
||||
) {
|
||||
throw new CompileException('Modifiers are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
|
||||
if (
|
||||
$node->args
|
||||
&& (!$begin || (is_string($begin) && strpos($begin, '%node') === false))
|
||||
&& (!$end || (is_string($end) && strpos($end, '%node') === false))
|
||||
&& (!$attr || (is_string($attr) && strpos($attr, '%node') === false))
|
||||
) {
|
||||
throw new CompileException('Arguments are not allowed in ' . $node->getNotation());
|
||||
}
|
||||
|
||||
if ($attr && $node->prefix === $node::PREFIX_NONE) {
|
||||
$node->empty = true;
|
||||
$node->context[1] = Latte\Compiler::CONTEXT_HTML_ATTRIBUTE;
|
||||
$res = $this->compile($node, $attr);
|
||||
if ($res === false) {
|
||||
return false;
|
||||
} elseif (!$node->attrCode) {
|
||||
$node->attrCode = "<?php $res ?>";
|
||||
}
|
||||
$node->context[1] = Latte\Compiler::CONTEXT_HTML_TEXT;
|
||||
|
||||
} elseif ($begin) {
|
||||
$res = $this->compile($node, $begin);
|
||||
if ($res === false || ($node->empty && $node->prefix)) {
|
||||
return false;
|
||||
} elseif (!$node->openingCode && is_string($res) && $res !== '') {
|
||||
$node->openingCode = "<?php $res ?>";
|
||||
}
|
||||
|
||||
} elseif (!$end) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Node is closed.
|
||||
* @return void
|
||||
*/
|
||||
public function nodeClosed(MacroNode $node)
|
||||
{
|
||||
if (isset($this->macros[$node->name][1])) {
|
||||
$res = $this->compile($node, $this->macros[$node->name][1]);
|
||||
if (!$node->closingCode && is_string($res) && $res !== '') {
|
||||
$node->closingCode = "<?php $res ?>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates code.
|
||||
* @return string|bool|null
|
||||
*/
|
||||
private function compile(MacroNode $node, $def)
|
||||
{
|
||||
$node->tokenizer->reset();
|
||||
$writer = Latte\PhpWriter::using($node);
|
||||
return is_string($def)
|
||||
? $writer->write($def)
|
||||
: $def($node, $writer);
|
||||
}
|
||||
|
||||
|
||||
public function getCompiler(): Latte\Compiler
|
||||
{
|
||||
return $this->compiler;
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
protected function checkExtraArgs(MacroNode $node): void
|
||||
{
|
||||
if ($node->tokenizer->isNext()) {
|
||||
$args = Latte\Runtime\Filters::truncate($node->tokenizer->joinAll(), 20);
|
||||
trigger_error("Unexpected arguments '$args' in " . $node->getNotation());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Smarter caching iterator.
|
||||
*
|
||||
* @property-read bool $first
|
||||
* @property-read bool $last
|
||||
* @property-read bool $empty
|
||||
* @property-read bool $odd
|
||||
* @property-read bool $even
|
||||
* @property-read int $counter
|
||||
* @property-read mixed $nextKey
|
||||
* @property-read mixed $nextValue
|
||||
* @internal
|
||||
*/
|
||||
class CachingIterator extends \CachingIterator implements \Countable
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var int */
|
||||
private $counter = 0;
|
||||
|
||||
|
||||
public function __construct($iterator)
|
||||
{
|
||||
if (is_array($iterator) || $iterator instanceof \stdClass) {
|
||||
$iterator = new \ArrayIterator($iterator);
|
||||
|
||||
} elseif ($iterator instanceof \IteratorAggregate) {
|
||||
do {
|
||||
$iterator = $iterator->getIterator();
|
||||
} while ($iterator instanceof \IteratorAggregate);
|
||||
|
||||
} elseif ($iterator instanceof \Traversable) {
|
||||
if (!$iterator instanceof \Iterator) {
|
||||
$iterator = new \IteratorIterator($iterator);
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid argument passed to foreach; array or Traversable expected, %s given.', is_object($iterator) ? get_class($iterator) : gettype($iterator)));
|
||||
}
|
||||
|
||||
parent::__construct($iterator, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the current element the first one?
|
||||
* @param int $width
|
||||
*/
|
||||
public function isFirst(int $width = null): bool
|
||||
{
|
||||
return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the current element the last one?
|
||||
* @param int $width
|
||||
*/
|
||||
public function isLast(int $width = null): bool
|
||||
{
|
||||
return !$this->hasNext() || ($width && ($this->counter % $width) === 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the iterator empty?
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->counter === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the counter odd?
|
||||
*/
|
||||
public function isOdd(): bool
|
||||
{
|
||||
return $this->counter % 2 === 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the counter even?
|
||||
*/
|
||||
public function isEven(): bool
|
||||
{
|
||||
return $this->counter % 2 === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the counter.
|
||||
*/
|
||||
public function getCounter(): int
|
||||
{
|
||||
return $this->counter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the counter as string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string) $this->counter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the count of elements.
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$inner = $this->getInnerIterator();
|
||||
if ($inner instanceof \Countable) {
|
||||
return $inner->count();
|
||||
|
||||
} else {
|
||||
throw new \LogicException('Iterator is not countable.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forwards to the next element.
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
parent::next();
|
||||
if (parent::valid()) {
|
||||
$this->counter++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rewinds the Iterator.
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
parent::rewind();
|
||||
$this->counter = parent::valid() ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next key.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextKey()
|
||||
{
|
||||
return $this->getInnerIterator()->key();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next element.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextValue()
|
||||
{
|
||||
return $this->getInnerIterator()->current();
|
||||
}
|
||||
|
||||
|
||||
/********************* property accessor ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns property value.
|
||||
* @throws \LogicException if the property is not defined.
|
||||
*/
|
||||
public function &__get($name)
|
||||
{
|
||||
if (method_exists($this, $m = 'get' . $name) || method_exists($this, $m = 'is' . $name)) {
|
||||
$ret = $this->$m();
|
||||
return $ret;
|
||||
}
|
||||
throw new \LogicException('Attempt to read undeclared property ' . get_class($this) . "::\$$name.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is property defined?
|
||||
*/
|
||||
public function __isset($name): bool
|
||||
{
|
||||
return method_exists($this, 'get' . $name) || method_exists($this, 'is' . $name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte\Engine;
|
||||
use Latte\Helpers;
|
||||
|
||||
|
||||
/**
|
||||
* Filter executor.
|
||||
* @internal
|
||||
*/
|
||||
class FilterExecutor
|
||||
{
|
||||
/** @var array */
|
||||
private $_dynamic = [];
|
||||
|
||||
/** @var array [name => [callback, FilterInfo aware] */
|
||||
private $_static = [
|
||||
'breaklines' => [[Filters::class, 'breaklines'], false],
|
||||
'bytes' => [[Filters::class, 'bytes'], false],
|
||||
'capitalize' => [[Filters::class, 'capitalize'], false],
|
||||
'datastream' => [[Filters::class, 'dataStream'], false],
|
||||
'date' => [[Filters::class, 'date'], false],
|
||||
'escapecss' => [[Filters::class, 'escapeCss'], false],
|
||||
'escapehtml' => [[Filters::class, 'escapeHtml'], false],
|
||||
'escapehtmlcomment' => [[Filters::class, 'escapeHtmlComment'], false],
|
||||
'escapeical' => [[Filters::class, 'escapeICal'], false],
|
||||
'escapejs' => [[Filters::class, 'escapeJs'], false],
|
||||
'escapeurl' => ['rawurlencode', false],
|
||||
'escapexml' => [[Filters::class, 'escapeXml'], false],
|
||||
'firstupper' => [[Filters::class, 'firstUpper'], false],
|
||||
'checkurl' => [[Filters::class, 'safeUrl'], false],
|
||||
'implode' => ['implode', false],
|
||||
'indent' => [[Filters::class, 'indent'], true],
|
||||
'length' => [[Filters::class, 'length'], false],
|
||||
'lower' => [[Filters::class, 'lower'], false],
|
||||
'number' => ['number_format', false],
|
||||
'padleft' => [[Filters::class, 'padLeft'], false],
|
||||
'padright' => [[Filters::class, 'padRight'], false],
|
||||
'repeat' => [[Filters::class, 'repeat'], true],
|
||||
'replace' => [[Filters::class, 'replace'], true],
|
||||
'replacere' => [[Filters::class, 'replaceRe'], false],
|
||||
'reverse' => [[Filters::class, 'reverse'], false],
|
||||
'strip' => [[Filters::class, 'strip'], true],
|
||||
'striphtml' => [[Filters::class, 'stripHtml'], true],
|
||||
'striptags' => [[Filters::class, 'stripTags'], true],
|
||||
'substr' => [[Filters::class, 'substring'], false],
|
||||
'trim' => [[Filters::class, 'trim'], true],
|
||||
'truncate' => [[Filters::class, 'truncate'], false],
|
||||
'upper' => [[Filters::class, 'upper'], false],
|
||||
'webalize' => [[\Nette\Utils\Strings::class, 'webalize'], false],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Registers run-time filter.
|
||||
* @return static
|
||||
*/
|
||||
public function add(?string $name, callable $callback)
|
||||
{
|
||||
if ($name == null) { // intentionally ==
|
||||
array_unshift($this->_dynamic, $callback);
|
||||
} else {
|
||||
$name = strtolower($name);
|
||||
$this->_static[$name] = [$callback, null];
|
||||
unset($this->$name);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all run-time filters.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return array_combine($tmp = array_keys($this->_static), $tmp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns filter for classic calling.
|
||||
*/
|
||||
public function __get($name): callable
|
||||
{
|
||||
$lname = strtolower($name);
|
||||
if (isset($this->$lname)) { // case mismatch
|
||||
return $this->$lname;
|
||||
|
||||
} elseif (isset($this->_static[$lname])) {
|
||||
[$callback, $aware] = $this->prepareFilter($lname);
|
||||
if ($aware) { // FilterInfo aware filter
|
||||
return $this->$lname = function (...$args) use ($callback) {
|
||||
array_unshift($args, $info = new FilterInfo);
|
||||
if ($args[1] instanceof IHtmlString) {
|
||||
$args[1] = $args[1]->__toString();
|
||||
$info->contentType = Engine::CONTENT_HTML;
|
||||
}
|
||||
$res = $callback(...$args);
|
||||
return $info->contentType === Engine::CONTENT_HTML
|
||||
? new Html($res)
|
||||
: $res;
|
||||
};
|
||||
} else { // classic filter
|
||||
return $this->$lname = $callback;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->$lname = function (...$args) use ($lname, $name) { // dynamic filter
|
||||
array_unshift($args, $lname);
|
||||
foreach ($this->_dynamic as $filter) {
|
||||
$res = (Helpers::checkCallback($filter))(...$args);
|
||||
if ($res !== null) {
|
||||
return $res;
|
||||
} elseif (isset($this->_static[$lname])) { // dynamic converted to classic
|
||||
$this->$name = Helpers::checkCallback($this->_static[$lname][0]);
|
||||
return ($this->$name)(...func_get_args());
|
||||
}
|
||||
}
|
||||
$hint = ($t = Helpers::getSuggestion(array_keys($this->_static), $name)) ? ", did you mean '$t'?" : '.';
|
||||
throw new \LogicException("Filter '$name' is not defined$hint");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls filter with FilterInfo.
|
||||
* @return mixed
|
||||
*/
|
||||
public function filterContent(string $name, FilterInfo $info, ...$args)
|
||||
{
|
||||
$lname = strtolower($name);
|
||||
if (!isset($this->_static[$lname])) {
|
||||
$hint = ($t = Helpers::getSuggestion(array_keys($this->_static), $name)) ? ", did you mean '$t'?" : '.';
|
||||
throw new \LogicException("Filter |$name is not defined$hint");
|
||||
}
|
||||
|
||||
[$callback, $aware] = $this->prepareFilter($lname);
|
||||
if ($aware) { // FilterInfo aware filter
|
||||
array_unshift($args, $info);
|
||||
return $callback(...$args);
|
||||
|
||||
} else { // classic filter
|
||||
if ($info->contentType !== Engine::CONTENT_TEXT) {
|
||||
trigger_error("Filter |$name is called with incompatible content type " . strtoupper($info->contentType)
|
||||
. ($info->contentType === Engine::CONTENT_HTML ? ', try to prepend |stripHtml.' : '.'), E_USER_WARNING);
|
||||
}
|
||||
$res = ($this->$name)(...$args);
|
||||
if ($res instanceof IHtmlString) {
|
||||
trigger_error("Filter |$name should be changed to content-aware filter.");
|
||||
$info->contentType = Engine::CONTENT_HTML;
|
||||
$res = $res->__toString();
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function prepareFilter(string $name): array
|
||||
{
|
||||
if (!isset($this->_static[$name][1])) {
|
||||
$callback = Helpers::checkCallback($this->_static[$name][0]);
|
||||
if (is_string($callback) && strpos($callback, '::')) {
|
||||
$callback = explode('::', $callback);
|
||||
} elseif (is_object($callback)) {
|
||||
$callback = [$callback, '__invoke'];
|
||||
}
|
||||
$ref = is_array($callback)
|
||||
? new \ReflectionMethod($callback[0], $callback[1])
|
||||
: new \ReflectionFunction($callback);
|
||||
$this->_static[$name][1] = ($tmp = $ref->getParameters())
|
||||
&& (string) $tmp[0]->getType() === FilterInfo::class;
|
||||
}
|
||||
return $this->_static[$name];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Filter runtime info
|
||||
*/
|
||||
class FilterInfo
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var string|null */
|
||||
public $contentType;
|
||||
|
||||
|
||||
public function __construct(string $contentType = null)
|
||||
{
|
||||
$this->contentType = $contentType;
|
||||
}
|
||||
}
|
||||
+690
@@ -0,0 +1,690 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte;
|
||||
use Latte\Engine;
|
||||
|
||||
|
||||
/**
|
||||
* Template filters. Uses UTF-8 only.
|
||||
* @internal
|
||||
*/
|
||||
class Filters
|
||||
{
|
||||
/** @deprecated */
|
||||
public static $dateFormat = '%x';
|
||||
|
||||
/** @internal @var bool use XHTML syntax? */
|
||||
public static $xhtml = false;
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside HTML.
|
||||
* @param mixed $s plain text
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function escapeHtml($s): string
|
||||
{
|
||||
return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside HTML.
|
||||
* @param mixed $s plain text or IHtmlString
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function escapeHtmlText($s): string
|
||||
{
|
||||
return $s instanceof IHtmlString || $s instanceof \Nette\Utils\IHtmlString
|
||||
? $s->__toString(true)
|
||||
: htmlspecialchars((string) $s, ENT_NOQUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside HTML attribute value.
|
||||
* @param mixed $s plain text
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function escapeHtmlAttr($s, bool $double = true): string
|
||||
{
|
||||
$double = $double && $s instanceof IHtmlString ? false : $double;
|
||||
$s = (string) $s;
|
||||
if (strpos($s, '`') !== false && strpbrk($s, ' <>"\'') === false) {
|
||||
$s .= ' '; // protection against innerHTML mXSS vulnerability nette/nette#1496
|
||||
}
|
||||
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', $double);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes HTML for use inside HTML attribute.
|
||||
* @param mixed $s HTML text
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function escapeHtmlAttrConv($s): string
|
||||
{
|
||||
return self::escapeHtmlAttr($s, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside HTML attribute name.
|
||||
* @param string $s plain text
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function escapeHtmlAttrUnquoted($s): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
return preg_match('#^[a-z0-9:-]+$#i', $s)
|
||||
? $s
|
||||
: '"' . self::escapeHtmlAttr($s) . '"';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside HTML comments.
|
||||
* @param string $s plain text
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function escapeHtmlComment($s): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
if ($s && ($s[0] === '-' || $s[0] === '>' || $s[0] === '!')) {
|
||||
$s = ' ' . $s;
|
||||
}
|
||||
$s = str_replace('--', '- - ', $s);
|
||||
if (substr($s, -1) === '-') {
|
||||
$s .= ' ';
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside XML 1.0 template.
|
||||
* @param string $s plain text
|
||||
* @return string XML
|
||||
*/
|
||||
public static function escapeXml($s): string
|
||||
{
|
||||
// XML 1.0: \x09 \x0A \x0D and C1 allowed directly, C0 forbidden
|
||||
// XML 1.1: \x00 forbidden directly and as a character reference,
|
||||
// \x09 \x0A \x0D \x85 allowed directly, C0, C1 and \x7F allowed as character references
|
||||
return htmlspecialchars(preg_replace('#[\x00-\x08\x0B\x0C\x0E-\x1F]+#', '', (string) $s), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside XML attribute name.
|
||||
* @param string $s plain text
|
||||
* @return string XML
|
||||
*/
|
||||
public static function escapeXmlAttrUnquoted($s): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
return preg_match('#^[a-z0-9:-]+$#i', $s)
|
||||
? $s
|
||||
: '"' . self::escapeXml($s) . '"';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside CSS template.
|
||||
* @param string $s plain text
|
||||
* @return string CSS
|
||||
*/
|
||||
public static function escapeCss($s): string
|
||||
{
|
||||
// http://www.w3.org/TR/2006/WD-CSS21-20060411/syndata.html#q6
|
||||
return addcslashes((string) $s, "\x00..\x1F!\"#$%&'()*+,./:;<=>?@[\\]^`{|}~");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes variables for use inside <script>.
|
||||
* @param mixed $s plain text
|
||||
* @return string JSON
|
||||
*/
|
||||
public static function escapeJs($s): string
|
||||
{
|
||||
if ($s instanceof IHtmlString || $s instanceof \Nette\Utils\IHtmlString) {
|
||||
$s = $s->__toString(true);
|
||||
}
|
||||
|
||||
$json = json_encode($s, JSON_UNESCAPED_UNICODE);
|
||||
if ($error = json_last_error()) {
|
||||
throw new \RuntimeException(json_last_error_msg(), $error);
|
||||
}
|
||||
|
||||
return str_replace(["\u{2028}", "\u{2029}", ']]>', '<!'], ['\u2028', '\u2029', ']]\x3E', '\x3C!'], $json);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes string for use inside iCal template.
|
||||
* @param string $s plain text
|
||||
*/
|
||||
public static function escapeICal($s): string
|
||||
{
|
||||
// https://www.ietf.org/rfc/rfc5545.txt
|
||||
return addcslashes(preg_replace('#[\x00-\x08\x0B\x0C-\x1F]+#', '', (string) $s), "\";\\,:\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes CSS/JS for usage in <script> and <style>..
|
||||
* @param string $s CSS/JS
|
||||
* @return string HTML RAWTEXT
|
||||
*/
|
||||
public static function escapeHtmlRawText($s): string
|
||||
{
|
||||
return preg_replace('#</(script|style)#i', '<\\/$1', (string) $s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts HTML to plain text.
|
||||
* @param string $s HTML
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function stripHtml(FilterInfo $info, $s): string
|
||||
{
|
||||
if (!in_array($info->contentType, [null, 'html', 'xhtml', 'htmlAttr', 'xhtmlAttr', 'xml', 'xmlAttr'], true)) {
|
||||
trigger_error('Filter |stripHtml used with incompatible type ' . strtoupper($info->contentType), E_USER_WARNING);
|
||||
}
|
||||
$info->contentType = Engine::CONTENT_TEXT;
|
||||
return html_entity_decode(strip_tags((string) $s), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes tags from HTML (but remains HTML entites).
|
||||
* @param string $s HTML
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function stripTags(FilterInfo $info, $s): string
|
||||
{
|
||||
if (!in_array($info->contentType, [null, 'html', 'xhtml', 'htmlAttr', 'xhtmlAttr', 'xml', 'xmlAttr'], true)) {
|
||||
trigger_error('Filter |stripTags used with incompatible type ' . strtoupper($info->contentType), E_USER_WARNING);
|
||||
}
|
||||
return strip_tags((string) $s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts ... to ...
|
||||
*/
|
||||
public static function convertTo(FilterInfo $info, string $dest, $s): string
|
||||
{
|
||||
$source = $info->contentType ?: Engine::CONTENT_TEXT;
|
||||
if ($source === $dest) {
|
||||
return $s;
|
||||
} elseif ($conv = self::getConvertor($source, $dest)) {
|
||||
$info->contentType = $dest;
|
||||
return $conv($s);
|
||||
} else {
|
||||
trigger_error('Filters: unable to convert content type ' . strtoupper($source) . ' to ' . strtoupper($dest), E_USER_WARNING);
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function getConvertor(string $source, string $dest): ?callable
|
||||
{
|
||||
static $table = [
|
||||
Engine::CONTENT_TEXT => [
|
||||
'html' => 'escapeHtmlText', 'xhtml' => 'escapeHtmlText',
|
||||
'htmlAttr' => 'escapeHtmlAttr', 'xhtmlAttr' => 'escapeHtmlAttr',
|
||||
'htmlAttrJs' => 'escapeHtmlAttr', 'xhtmlAttrJs' => 'escapeHtmlAttr',
|
||||
'htmlAttrCss' => 'escapeHtmlAttr', 'xhtmlAttrCss' => 'escapeHtmlAttr',
|
||||
'htmlAttrUrl' => 'escapeHtmlAttr', 'xhtmlAttrUrl' => 'escapeHtmlAttr',
|
||||
'htmlComment' => 'escapeHtmlComment', 'xhtmlComment' => 'escapeHtmlComment',
|
||||
'xml' => 'escapeXml', 'xmlAttr' => 'escapeXml',
|
||||
],
|
||||
Engine::CONTENT_JS => [
|
||||
'html' => 'escapeHtmlText', 'xhtml' => 'escapeHtmlText',
|
||||
'htmlAttr' => 'escapeHtmlAttr', 'xhtmlAttr' => 'escapeHtmlAttr',
|
||||
'htmlAttrJs' => 'escapeHtmlAttr', 'xhtmlAttrJs' => 'escapeHtmlAttr',
|
||||
'htmlJs' => 'escapeHtmlRawText', 'xhtmlJs' => 'escapeHtmlRawText',
|
||||
'htmlComment' => 'escapeHtmlComment', 'xhtmlComment' => 'escapeHtmlComment',
|
||||
],
|
||||
Engine::CONTENT_CSS => [
|
||||
'html' => 'escapeHtmlText', 'xhtml' => 'escapeHtmlText',
|
||||
'htmlAttr' => 'escapeHtmlAttr', 'xhtmlAttr' => 'escapeHtmlAttr',
|
||||
'htmlAttrCss' => 'escapeHtmlAttr', 'xhtmlAttrCss' => 'escapeHtmlAttr',
|
||||
'htmlCss' => 'escapeHtmlRawText', 'xhtmlCss' => 'escapeHtmlRawText',
|
||||
'htmlComment' => 'escapeHtmlComment', 'xhtmlComment' => 'escapeHtmlComment',
|
||||
],
|
||||
Engine::CONTENT_HTML => [
|
||||
'htmlAttr' => 'escapeHtmlAttrConv',
|
||||
'htmlAttrJs' => 'escapeHtmlAttrConv',
|
||||
'htmlAttrCss' => 'escapeHtmlAttrConv',
|
||||
'htmlAttrUrl' => 'escapeHtmlAttrConv',
|
||||
'htmlComment' => 'escapeHtmlComment',
|
||||
],
|
||||
Engine::CONTENT_XHTML => [
|
||||
'xhtmlAttr' => 'escapeHtmlAttrConv',
|
||||
'xhtmlAttrJs' => 'escapeHtmlAttrConv',
|
||||
'xhtmlAttrCss' => 'escapeHtmlAttrConv',
|
||||
'xhtmlAttrUrl' => 'escapeHtmlAttrConv',
|
||||
'xhtmlComment' => 'escapeHtmlComment',
|
||||
],
|
||||
];
|
||||
return isset($table[$source][$dest]) ? [self::class, $table[$source][$dest]] : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sanitizes string for use inside href attribute.
|
||||
* @param string $s plain text
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function safeUrl($s): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
return preg_match('~^(?:(?:https?|ftp)://[^@]+(?:/.*)?|mailto:.+|[/?#].*|[^:]+)\z~i', $s) ? $s : '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all repeated white spaces with a single space.
|
||||
* @param string $s text|HTML
|
||||
* @return string text|HTML
|
||||
*/
|
||||
public static function strip(FilterInfo $info, string $s): string
|
||||
{
|
||||
return in_array($info->contentType, [Engine::CONTENT_HTML, Engine::CONTENT_XHTML], true)
|
||||
? trim(self::spacelessHtml($s))
|
||||
: trim(self::spacelessText($s));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all repeated white spaces with a single space.
|
||||
* @param string $s HTML
|
||||
* @param int $phase output buffering phase
|
||||
* @param bool $strip stripping mode
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function spacelessHtml(string $s, int $phase = null, bool &$strip = true): string
|
||||
{
|
||||
if ($phase & PHP_OUTPUT_HANDLER_START) {
|
||||
$s = ltrim($s);
|
||||
}
|
||||
if ($phase & PHP_OUTPUT_HANDLER_FINAL) {
|
||||
$s = rtrim($s);
|
||||
}
|
||||
return preg_replace_callback(
|
||||
'#[ \t\r\n]+|<(/)?(textarea|pre|script)(?=\W)#si',
|
||||
function ($m) use (&$strip) {
|
||||
if (empty($m[2])) {
|
||||
return $strip ? ' ' : $m[0];
|
||||
} else {
|
||||
$strip = !empty($m[1]);
|
||||
return $m[0];
|
||||
}
|
||||
},
|
||||
$s
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all repeated white spaces with a single space.
|
||||
* @return string text
|
||||
*/
|
||||
public static function spacelessText(string $s): string
|
||||
{
|
||||
return preg_replace('#[ \t\r\n]+#', ' ', $s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indents plain text or HTML the content from the left.
|
||||
*/
|
||||
public static function indent(FilterInfo $info, string $s, int $level = 1, string $chars = "\t"): string
|
||||
{
|
||||
if ($level < 1) {
|
||||
// do nothing
|
||||
} elseif (in_array($info->contentType, [Engine::CONTENT_HTML, Engine::CONTENT_XHTML], true)) {
|
||||
$s = preg_replace_callback('#<(textarea|pre).*?</\\1#si', function ($m) {
|
||||
return strtr($m[0], " \t\r\n", "\x1F\x1E\x1D\x1A");
|
||||
}, $s);
|
||||
if (preg_last_error()) {
|
||||
throw new Latte\RegexpException(null, preg_last_error());
|
||||
}
|
||||
$s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
|
||||
$s = strtr($s, "\x1F\x1E\x1D\x1A", " \t\r\n");
|
||||
} else {
|
||||
$s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Repeats text.
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function repeat(FilterInfo $info, $s, int $count): string
|
||||
{
|
||||
return str_repeat((string) $s, $count);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Date/time formatting.
|
||||
* @param string|int|\DateTimeInterface|\DateInterval $time
|
||||
*/
|
||||
public static function date($time, string $format = null): ?string
|
||||
{
|
||||
if ($time == null) { // intentionally ==
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($format)) {
|
||||
$format = self::$dateFormat;
|
||||
}
|
||||
|
||||
if ($time instanceof \DateInterval) {
|
||||
return $time->format($format);
|
||||
|
||||
} elseif (is_numeric($time)) {
|
||||
$time = new \DateTime('@' . $time);
|
||||
$time->setTimeZone(new \DateTimeZone(date_default_timezone_get()));
|
||||
|
||||
} elseif (!$time instanceof \DateTimeInterface) {
|
||||
$time = new \DateTime($time);
|
||||
}
|
||||
return strpos($format, '%') === false
|
||||
? $time->format($format) // formats using date()
|
||||
: strftime($format, $time->format('U') + 0); // formats according to locales
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts to human readable file size.
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function bytes(float $bytes, int $precision = 2): string
|
||||
{
|
||||
$bytes = round($bytes);
|
||||
$units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
|
||||
foreach ($units as $unit) {
|
||||
if (abs($bytes) < 1024 || $unit === end($units)) {
|
||||
break;
|
||||
}
|
||||
$bytes = $bytes / 1024;
|
||||
}
|
||||
return round($bytes, $precision) . ' ' . $unit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs a search and replace.
|
||||
*/
|
||||
public static function replace(FilterInfo $info, $subject, string $search, string $replacement = ''): string
|
||||
{
|
||||
return str_replace($search, $replacement, (string) $subject);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform a regular expression search and replace.
|
||||
*/
|
||||
public static function replaceRe(string $subject, string $pattern, string $replacement = ''): string
|
||||
{
|
||||
$res = preg_replace($pattern, $replacement, $subject);
|
||||
if (preg_last_error()) {
|
||||
throw new Latte\RegexpException(null, preg_last_error());
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The data: URI generator.
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function dataStream(string $data, string $type = null): string
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
|
||||
}
|
||||
return 'data:' . ($type ? "$type;" : '') . 'base64,' . base64_encode($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $s plain text
|
||||
*/
|
||||
public static function breaklines($s): Html
|
||||
{
|
||||
return new Html(nl2br(htmlspecialchars((string) $s, ENT_NOQUOTES, 'UTF-8'), self::$xhtml));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a part of string.
|
||||
*/
|
||||
public static function substring($s, int $start, int $length = null): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
if ($length === null) {
|
||||
$length = self::strLength($s);
|
||||
}
|
||||
if (function_exists('mb_substr')) {
|
||||
return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster
|
||||
}
|
||||
return iconv_substr($s, $start, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Truncates string to maximal length.
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function truncate($s, $maxLen, $append = "\u{2026}"): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
if (self::strLength($s) > $maxLen) {
|
||||
$maxLen = $maxLen - self::strLength($append);
|
||||
if ($maxLen < 1) {
|
||||
return $append;
|
||||
|
||||
} elseif (preg_match('#^.{1,' . $maxLen . '}(?=[\s\x00-/:-@\[-`{-~])#us', $s, $matches)) {
|
||||
return $matches[0] . $append;
|
||||
|
||||
} else {
|
||||
return self::substring($s, 0, $maxLen) . $append;
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to lower case.
|
||||
* @param string $s plain text
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function lower($s): string
|
||||
{
|
||||
return mb_strtolower((string) $s, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to upper case.
|
||||
* @param string $s plain text
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function upper($s): string
|
||||
{
|
||||
return mb_strtoupper((string) $s, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert first character to upper case.
|
||||
* @param string $s plain text
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function firstUpper($s): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Capitalize string.
|
||||
* @param string $s plain text
|
||||
* @return string plain text
|
||||
*/
|
||||
public static function capitalize($s): string
|
||||
{
|
||||
return mb_convert_case((string) $s, MB_CASE_TITLE, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns length of string or iterable.
|
||||
* @param array|\Countable|\Traversable|string $val
|
||||
*/
|
||||
public static function length($val): int
|
||||
{
|
||||
if (is_array($val) || $val instanceof \Countable) {
|
||||
return count($val);
|
||||
} elseif ($val instanceof \Traversable) {
|
||||
return iterator_count($val);
|
||||
} else {
|
||||
return self::strLength($val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static function strLength(string $s): int
|
||||
{
|
||||
return function_exists('mb_strlen') ? mb_strlen($s, 'UTF-8') : strlen(utf8_decode($s));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strips whitespace.
|
||||
* @return string
|
||||
*/
|
||||
public static function trim(FilterInfo $info, $s, string $charlist = " \t\n\r\0\x0B\u{A0}"): string
|
||||
{
|
||||
$charlist = preg_quote($charlist, '#');
|
||||
$s = preg_replace('#^[' . $charlist . ']+|[' . $charlist . ']+\z#u', '', (string) $s);
|
||||
if (preg_last_error()) {
|
||||
throw new Latte\RegexpException(null, preg_last_error());
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pad a string to a certain length with another string.
|
||||
*/
|
||||
public static function padLeft($s, int $length, string $pad = ' '): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
$length = max(0, $length - self::strLength($s));
|
||||
$padLen = self::strLength($pad);
|
||||
return str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen) . $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pad a string to a certain length with another string.
|
||||
*/
|
||||
public static function padRight($s, int $length, string $pad = ' '): string
|
||||
{
|
||||
$s = (string) $s;
|
||||
$length = max(0, $length - self::strLength($s));
|
||||
$padLen = self::strLength($pad);
|
||||
return $s . str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverses string or array.
|
||||
* @param string|array|\Traversable $val
|
||||
*/
|
||||
public static function reverse($val, bool $preserveKeys = false)
|
||||
{
|
||||
if (is_array($val)) {
|
||||
return array_reverse($val, $preserveKeys);
|
||||
} elseif ($val instanceof \Traversable) {
|
||||
return array_reverse(iterator_to_array($val), $preserveKeys);
|
||||
} else {
|
||||
return iconv('UTF-32LE', 'UTF-8', strrev(iconv('UTF-8', 'UTF-32BE', (string) $val)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns element's attributes.
|
||||
*/
|
||||
public static function htmlAttributes($attrs): string
|
||||
{
|
||||
if (!is_array($attrs)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = '';
|
||||
foreach ($attrs as $key => $value) {
|
||||
if ($value === null || $value === false) {
|
||||
continue;
|
||||
|
||||
} elseif ($value === true) {
|
||||
if (static::$xhtml) {
|
||||
$s .= ' ' . $key . '="' . $key . '"';
|
||||
} else {
|
||||
$s .= ' ' . $key;
|
||||
}
|
||||
continue;
|
||||
|
||||
} elseif (is_array($value)) {
|
||||
$tmp = null;
|
||||
foreach ($value as $k => $v) {
|
||||
if ($v != null) { // intentionally ==, skip nulls & empty string
|
||||
// composite 'style' vs. 'others'
|
||||
$tmp[] = $v === true ? $k : (is_string($k) ? $k . ':' . $v : $v);
|
||||
}
|
||||
}
|
||||
if ($tmp === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
|
||||
|
||||
} else {
|
||||
$value = (string) $value;
|
||||
}
|
||||
|
||||
$q = strpos($value, '"') === false ? '"' : "'";
|
||||
$s .= ' ' . $key . '=' . $q
|
||||
. str_replace(
|
||||
['&', $q, '<'],
|
||||
['&', $q === '"' ? '"' : ''', self::$xhtml ? '<' : '<'],
|
||||
$value
|
||||
)
|
||||
. (strpos($value, '`') !== false && strpbrk($value, ' <>"\'') === false ? ' ' : '')
|
||||
. $q;
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte;
|
||||
|
||||
|
||||
/**
|
||||
* HTML literal.
|
||||
*/
|
||||
class Html implements IHtmlString
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var string */
|
||||
private $value;
|
||||
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = (string) $value;
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
|
||||
interface IHtmlString
|
||||
{
|
||||
|
||||
/**
|
||||
* @return string in HTML format
|
||||
*/
|
||||
function __toString(): string;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
|
||||
/**
|
||||
* Snippet bridge
|
||||
* @internal
|
||||
*/
|
||||
interface ISnippetBridge
|
||||
{
|
||||
function isSnippetMode();
|
||||
|
||||
function setSnippetMode($snippetMode);
|
||||
|
||||
function needsRedraw($name);
|
||||
|
||||
function markRedrawn($name);
|
||||
|
||||
function getHtmlId($name);
|
||||
|
||||
function addSnippet($name, $content);
|
||||
|
||||
function renderChildren();
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte;
|
||||
|
||||
|
||||
/**
|
||||
* Snippet driver
|
||||
* @internal
|
||||
*/
|
||||
class SnippetDriver
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
public const
|
||||
TYPE_STATIC = 'static',
|
||||
TYPE_DYNAMIC = 'dynamic',
|
||||
TYPE_AREA = 'area';
|
||||
|
||||
/** @var array */
|
||||
private $stack = [];
|
||||
|
||||
/** @var int */
|
||||
private $nestingLevel = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $renderingSnippets = false;
|
||||
|
||||
/** @var ISnippetBridge */
|
||||
private $bridge;
|
||||
|
||||
|
||||
public function __construct(ISnippetBridge $bridge)
|
||||
{
|
||||
$this->bridge = $bridge;
|
||||
}
|
||||
|
||||
|
||||
public function enter(string $name, string $type): void
|
||||
{
|
||||
if (!$this->renderingSnippets) {
|
||||
return;
|
||||
}
|
||||
$obStarted = false;
|
||||
if (
|
||||
($this->nestingLevel === 0 && $this->bridge->needsRedraw($name))
|
||||
|| ($type === self::TYPE_DYNAMIC && ($previous = end($this->stack)) && $previous[1] === true)
|
||||
) {
|
||||
ob_start(function () {});
|
||||
$this->nestingLevel = $type === self::TYPE_AREA ? 0 : 1;
|
||||
$obStarted = true;
|
||||
} elseif ($this->nestingLevel > 0) {
|
||||
$this->nestingLevel++;
|
||||
}
|
||||
$this->stack[] = [$name, $obStarted];
|
||||
$this->bridge->markRedrawn($name);
|
||||
}
|
||||
|
||||
|
||||
public function leave(): void
|
||||
{
|
||||
if (!$this->renderingSnippets) {
|
||||
return;
|
||||
}
|
||||
[$name, $obStarted] = array_pop($this->stack);
|
||||
if ($this->nestingLevel > 0 && --$this->nestingLevel === 0) {
|
||||
$content = ob_get_clean();
|
||||
$this->bridge->addSnippet($name, $content);
|
||||
} elseif ($obStarted) { // dynamic snippet wrapper or snippet area
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getHtmlId(string $name): string
|
||||
{
|
||||
return $this->bridge->getHtmlId($name);
|
||||
}
|
||||
|
||||
|
||||
public function renderSnippets(array $blocks, array $params): bool
|
||||
{
|
||||
if ($this->renderingSnippets || !$this->bridge->isSnippetMode()) {
|
||||
return false;
|
||||
}
|
||||
$this->renderingSnippets = true;
|
||||
$this->bridge->setSnippetMode(false);
|
||||
foreach ($blocks as $name => $function) {
|
||||
if ($name[0] !== '_' || !$this->bridge->needsRedraw(substr($name, 1))) {
|
||||
continue;
|
||||
}
|
||||
$function = reset($function);
|
||||
$function($params);
|
||||
}
|
||||
$this->bridge->setSnippetMode(true);
|
||||
$this->bridge->renderChildren();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte\Runtime;
|
||||
|
||||
use Latte;
|
||||
use Latte\Engine;
|
||||
|
||||
|
||||
/**
|
||||
* Template.
|
||||
*/
|
||||
class Template
|
||||
{
|
||||
use Latte\Strict;
|
||||
|
||||
/** @var \stdClass global accumulators for intermediate results */
|
||||
public $global;
|
||||
|
||||
/** @var string @internal */
|
||||
protected $contentType = Engine::CONTENT_HTML;
|
||||
|
||||
/** @var array @internal */
|
||||
protected $params = [];
|
||||
|
||||
/** @var FilterExecutor */
|
||||
protected $filters;
|
||||
|
||||
/** @var array [name => method] @internal */
|
||||
protected $blocks = [];
|
||||
|
||||
/** @var string|null|false @internal */
|
||||
protected $parentName;
|
||||
|
||||
/** @var [name => [callbacks]] @internal */
|
||||
protected $blockQueue = [];
|
||||
|
||||
/** @var [name => type] @internal */
|
||||
protected $blockTypes = [];
|
||||
|
||||
/** @var Engine */
|
||||
private $engine;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var Template|null @internal */
|
||||
private $referringTemplate;
|
||||
|
||||
/** @var string|null @internal */
|
||||
private $referenceType;
|
||||
|
||||
|
||||
public function __construct(Engine $engine, array $params, FilterExecutor $filters, array $providers, string $name)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
$this->params = $params;
|
||||
$this->filters = $filters;
|
||||
$this->name = $name;
|
||||
$this->global = (object) $providers;
|
||||
foreach ($this->blocks as $nm => $method) {
|
||||
$this->blockQueue[$nm][] = [$this, $method];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns array of all parameters.
|
||||
*/
|
||||
public function getParameters(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns parameter.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParameter(string $name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->params)) {
|
||||
trigger_error("The variable '$name' does not exist in template.", E_USER_NOTICE);
|
||||
}
|
||||
return $this->params[$name];
|
||||
}
|
||||
|
||||
|
||||
public function getContentType(): string
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
|
||||
public function getParentName(): ?string
|
||||
{
|
||||
return $this->parentName ?: null;
|
||||
}
|
||||
|
||||
|
||||
public function getReferringTemplate(): ?self
|
||||
{
|
||||
return $this->referringTemplate;
|
||||
}
|
||||
|
||||
|
||||
public function getReferenceType(): ?string
|
||||
{
|
||||
return $this->referenceType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders template.
|
||||
* @internal
|
||||
*/
|
||||
public function render(): void
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
if ($this->parentName === null && isset($this->global->coreParentFinder)) {
|
||||
$this->parentName = ($this->global->coreParentFinder)($this);
|
||||
}
|
||||
if (isset($this->global->snippetBridge) && !isset($this->global->snippetDriver)) {
|
||||
$this->global->snippetDriver = new SnippetDriver($this->global->snippetBridge);
|
||||
}
|
||||
Filters::$xhtml = (bool) preg_match('#xml|xhtml#', $this->contentType);
|
||||
|
||||
if ($this->referenceType === 'import') {
|
||||
if ($this->parentName) {
|
||||
$this->createTemplate($this->parentName, [], 'import')->render();
|
||||
}
|
||||
return;
|
||||
|
||||
} elseif ($this->parentName) { // extends
|
||||
ob_start(function () {});
|
||||
$params = $this->main();
|
||||
ob_end_clean();
|
||||
$this->createTemplate($this->parentName, $params, 'extends')->render();
|
||||
return;
|
||||
|
||||
} elseif (!empty($this->params['_renderblock'])) { // single block rendering
|
||||
$tmp = $this;
|
||||
while (in_array($this->referenceType, ['extends', null], true) && ($tmp = $tmp->referringTemplate));
|
||||
if (!$tmp) {
|
||||
$this->renderBlock($this->params['_renderblock'], $this->params);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// old accumulators for back compatibility
|
||||
$this->params['_l'] = new \stdClass;
|
||||
$this->params['_g'] = $this->global;
|
||||
$this->params['_b'] = (object) ['blocks' => &$this->blockQueue, 'types' => &$this->blockTypes];
|
||||
if (isset($this->global->snippetDriver) && $this->global->snippetBridge->isSnippetMode()) {
|
||||
if ($this->global->snippetDriver->renderSnippets($this->blockQueue, $this->params)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->main();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders template.
|
||||
* @internal
|
||||
*/
|
||||
public function createTemplate(string $name, array $params, string $referenceType): self
|
||||
{
|
||||
$name = $this->engine->getLoader()->getReferredName($name, $this->name);
|
||||
$child = $this->engine->createTemplate($name, $params);
|
||||
$child->referringTemplate = $this;
|
||||
$child->referenceType = $referenceType;
|
||||
$child->global = $this->global;
|
||||
if (in_array($referenceType, ['extends', 'includeblock', 'import'], true)) {
|
||||
$this->blockQueue = array_merge_recursive($this->blockQueue, $child->blockQueue);
|
||||
foreach ($child->blockTypes as $nm => $type) {
|
||||
$this->checkBlockContentType($type, $nm);
|
||||
}
|
||||
$child->blockQueue = &$this->blockQueue;
|
||||
$child->blockTypes = &$this->blockTypes;
|
||||
}
|
||||
return $child;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|\Closure $mod content-type name or modifier closure
|
||||
* @internal
|
||||
*/
|
||||
public function renderToContentType($mod): void
|
||||
{
|
||||
if ($mod instanceof \Closure) {
|
||||
echo $mod($this->capture([$this, 'render']), $this->contentType);
|
||||
} elseif ($mod && $mod !== $this->contentType) {
|
||||
if ($filter = Filters::getConvertor($this->contentType, $mod)) {
|
||||
echo $filter($this->capture([$this, 'render']));
|
||||
} else {
|
||||
trigger_error("Including '$this->name' with content type " . strtoupper($this->contentType) . ' into incompatible type ' . strtoupper($mod) . '.', E_USER_WARNING);
|
||||
}
|
||||
} else {
|
||||
$this->render();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function prepare()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/********************* blocks ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Renders block.
|
||||
* @param string|\Closure $mod content-type name or modifier closure
|
||||
* @internal
|
||||
*/
|
||||
public function renderBlock(string $name, array $params, $mod = null): void
|
||||
{
|
||||
if (empty($this->blockQueue[$name])) {
|
||||
$hint = isset($this->blockQueue) && ($t = Latte\Helpers::getSuggestion(array_keys($this->blockQueue), $name)) ? ", did you mean '$t'?" : '.';
|
||||
throw new \RuntimeException("Cannot include undefined block '$name'$hint");
|
||||
}
|
||||
|
||||
$block = reset($this->blockQueue[$name]);
|
||||
if ($mod && $mod !== ($blockType = $this->blockTypes[$name])) {
|
||||
if ($filter = (is_string($mod) ? Filters::getConvertor($blockType, $mod) : $mod)) {
|
||||
echo $filter($this->capture(function () use ($block, $params): void { $block($params); }), $blockType);
|
||||
return;
|
||||
}
|
||||
trigger_error("Including block $name with content type " . strtoupper($blockType) . ' into incompatible type ' . strtoupper($mod) . '.', E_USER_WARNING);
|
||||
}
|
||||
$block($params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders parent block.
|
||||
* @internal
|
||||
*/
|
||||
public function renderBlockParent(string $name, array $params): void
|
||||
{
|
||||
if (empty($this->blockQueue[$name]) || ($block = next($this->blockQueue[$name])) === false) {
|
||||
throw new \RuntimeException("Cannot include undefined parent block '$name'.");
|
||||
}
|
||||
$block($params);
|
||||
prev($this->blockQueue[$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function checkBlockContentType(string $current, string $name): void
|
||||
{
|
||||
$expected = &$this->blockTypes[$name];
|
||||
if ($expected === null) {
|
||||
$expected = $current;
|
||||
} elseif ($expected !== $current) {
|
||||
trigger_error("Overridden block $name with content type " . strtoupper($current) . ' by incompatible type ' . strtoupper($expected) . '.', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Captures output to string.
|
||||
* @internal
|
||||
*/
|
||||
public function capture(callable $function): string
|
||||
{
|
||||
try {
|
||||
ob_start(function () {});
|
||||
$this->global->coreCaptured = true;
|
||||
$function();
|
||||
return ob_get_clean();
|
||||
} catch (\Throwable $e) {
|
||||
ob_end_clean();
|
||||
throw $e;
|
||||
} finally {
|
||||
$this->global->coreCaptured = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
use LogicException;
|
||||
|
||||
|
||||
/**
|
||||
* Better OOP experience.
|
||||
*/
|
||||
trait Strict
|
||||
{
|
||||
|
||||
/**
|
||||
* Call to undefined method.
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
{
|
||||
$class = method_exists($this, $name) ? 'parent' : get_class($this);
|
||||
$items = (new \ReflectionClass($this))->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
|
||||
throw new LogicException("Call to undefined method $class::$name()$hint");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to undefined static method.
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function __callStatic(string $name, array $args)
|
||||
{
|
||||
$rc = new \ReflectionClass(get_called_class());
|
||||
$items = array_intersect($rc->getMethods(\ReflectionMethod::IS_PUBLIC), $rc->getMethods(\ReflectionMethod::IS_STATIC));
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
|
||||
throw new LogicException("Call to undefined static method {$rc->getName()}::$name()$hint");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Access to undeclared property.
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function &__get(string $name)
|
||||
{
|
||||
$rc = new \ReflectionClass($this);
|
||||
$items = array_diff($rc->getProperties(\ReflectionProperty::IS_PUBLIC), $rc->getProperties(\ReflectionProperty::IS_STATIC));
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
|
||||
throw new LogicException("Attempt to read undeclared property {$rc->getName()}::$$name$hint");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Access to undeclared property.
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function __set(string $name, $value)
|
||||
{
|
||||
$rc = new \ReflectionClass($this);
|
||||
$items = array_diff($rc->getProperties(\ReflectionProperty::IS_PUBLIC), $rc->getProperties(\ReflectionProperty::IS_STATIC));
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
|
||||
throw new LogicException("Attempt to write to undeclared property {$rc->getName()}::$$name$hint");
|
||||
}
|
||||
|
||||
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Access to undeclared property.
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function __unset(string $name)
|
||||
{
|
||||
$class = get_class($this);
|
||||
throw new LogicException("Attempt to unset undeclared property $class::$$name.");
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Latte (https://latte.nette.org)
|
||||
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Latte;
|
||||
|
||||
|
||||
/**
|
||||
* The exception occured during Latte compilation.
|
||||
*/
|
||||
class CompileException extends \Exception
|
||||
{
|
||||
/** @var string */
|
||||
public $sourceCode;
|
||||
|
||||
/** @var string */
|
||||
public $sourceName;
|
||||
|
||||
/** @var int */
|
||||
public $sourceLine;
|
||||
|
||||
|
||||
public function setSource(string $code, int $line, string $name = null)
|
||||
{
|
||||
$this->sourceCode = $code;
|
||||
$this->sourceLine = $line;
|
||||
$this->sourceName = $name;
|
||||
if (@is_file($name)) { // @ - may trigger error
|
||||
$this->message = rtrim($this->message, '.')
|
||||
. ' in ' . str_replace(dirname(dirname($name)), '...', $name) . ($line ? ":$line" : '');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of the last Regexp execution.
|
||||
*/
|
||||
class RegexpException extends \Exception
|
||||
{
|
||||
public const MESSAGES = [
|
||||
PREG_INTERNAL_ERROR => 'Internal error',
|
||||
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
|
||||
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
|
||||
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
|
||||
PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point',
|
||||
6 => 'Failed due to limited JIT stack space', // PREG_JIT_STACKLIMIT_ERROR
|
||||
];
|
||||
|
||||
|
||||
public function __construct($message, $code = null)
|
||||
{
|
||||
parent::__construct($message ?: (self::MESSAGES[$code] ?? 'Unknown error'), $code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error during rendering template.
|
||||
*/
|
||||
class RuntimeException extends \Exception
|
||||
{
|
||||
}
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
spl_autoload_register(function ($className) {
|
||||
static $classMap = [
|
||||
Latte\CompileException::class => 'exceptions.php',
|
||||
Latte\Compiler::class => 'Compiler/Compiler.php',
|
||||
Latte\Engine::class => 'Engine.php',
|
||||
Latte\Helpers::class => 'Helpers.php',
|
||||
Latte\HtmlNode::class => 'Compiler/HtmlNode.php',
|
||||
Latte\ILoader::class => 'ILoader.php',
|
||||
Latte\IMacro::class => 'IMacro.php',
|
||||
Latte\Loaders\FileLoader::class => 'Loaders/FileLoader.php',
|
||||
Latte\Loaders\StringLoader::class => 'Loaders/StringLoader.php',
|
||||
Latte\MacroNode::class => 'Compiler/MacroNode.php',
|
||||
Latte\Macros\BlockMacros::class => 'Macros/BlockMacros.php',
|
||||
Latte\Macros\CoreMacros::class => 'Macros/CoreMacros.php',
|
||||
Latte\Macros\MacroSet::class => 'Macros/MacroSet.php',
|
||||
Latte\MacroTokens::class => 'Compiler/MacroTokens.php',
|
||||
Latte\Parser::class => 'Compiler/Parser.php',
|
||||
Latte\PhpHelpers::class => 'Compiler/PhpHelpers.php',
|
||||
Latte\PhpWriter::class => 'Compiler/PhpWriter.php',
|
||||
Latte\RegexpException::class => 'exceptions.php',
|
||||
Latte\Runtime\CachingIterator::class => 'Runtime/CachingIterator.php',
|
||||
Latte\Runtime\FilterExecutor::class => 'Runtime/FilterExecutor.php',
|
||||
Latte\Runtime\FilterInfo::class => 'Runtime/FilterInfo.php',
|
||||
Latte\Runtime\Filters::class => 'Runtime/Filters.php',
|
||||
Latte\Runtime\Html::class => 'Runtime/Html.php',
|
||||
Latte\Runtime\IHtmlString::class => 'Runtime/IHtmlString.php',
|
||||
Latte\Runtime\ISnippetBridge::class => 'Runtime/ISnippetBridge.php',
|
||||
Latte\Runtime\SnippetDriver::class => 'Runtime/SnippetDriver.php',
|
||||
Latte\Runtime\Template::class => 'Runtime/Template.php',
|
||||
Latte\RuntimeException::class => 'exceptions.php',
|
||||
Latte\Strict::class => 'Strict.php',
|
||||
Latte\Token::class => 'Compiler/Token.php',
|
||||
Latte\TokenIterator::class => 'Compiler/TokenIterator.php',
|
||||
Latte\Tokenizer::class => 'Compiler/Tokenizer.php',
|
||||
];
|
||||
|
||||
if (isset($classMap[$className])) {
|
||||
require __DIR__ . '/Latte/' . $classMap[$className];
|
||||
}
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "nette/application",
|
||||
"description": "🏆 Nette Application: a full-stack component-based MVC kernel for PHP that helps you write powerful and modern web applications. Write less, have cleaner code and your work will bring you joy.",
|
||||
"keywords": ["nette", "mvc", "framework", "component-based", "routing", "seo", "mvp", "presenter", "control", "forms"],
|
||||
"homepage": "https://nette.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"nette/component-model": "^2.3",
|
||||
"nette/http": "^2.2",
|
||||
"nette/reflection": "^2.2",
|
||||
"nette/utils": "^2.4 || ~3.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"nette/forms": "Allows to use Nette\\Application\\UI\\Form",
|
||||
"latte/latte": "Allows using Latte in templates"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.0",
|
||||
"nette/di": "^2.4",
|
||||
"nette/forms": "^2.4",
|
||||
"nette/robot-loader": "^2.4.2 || ^3.0",
|
||||
"nette/security": "^2.4",
|
||||
"latte/latte": "^2.4.3",
|
||||
"tracy/tracy": "^2.4",
|
||||
"mockery/mockery": "^0.9.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/nette": "<2.2",
|
||||
"nette/di": "<2.4",
|
||||
"nette/forms": "<2.4",
|
||||
"nette/latte": "<2.4"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"],
|
||||
"files": ["src/compatibility.php"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Framework under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
The BSD License is recommended for most projects. It is easy to understand and it
|
||||
places almost no restrictions on what you can do with the framework. If the GPL
|
||||
fits better to your project, you can use the framework under this license.
|
||||
|
||||
You don't have to notify anyone which license you are using. You can freely
|
||||
use Nette Framework in commercial projects as long as the copyright header
|
||||
remains intact.
|
||||
|
||||
Please be advised that the name "Nette Framework" is a protected trademark and its
|
||||
usage has some limitations. So please do not use word "Nette" in the name of your
|
||||
project or top-level domain, and choose a name that stands on its own merits.
|
||||
If your stuff is good, it will not take long to establish a reputation for yourselves.
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Framework" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
Nette Application MVC
|
||||
=====================
|
||||
|
||||
[](https://packagist.org/packages/nette/application)
|
||||
[](https://travis-ci.org/nette/application)
|
||||
[](https://coveralls.io/github/nette/application?branch=master)
|
||||
[](https://github.com/nette/application/releases)
|
||||
[](https://github.com/nette/application/blob/master/license.md)
|
||||
|
||||
Model-View-Controller is a software architecture that was created to satisfy the need to separate utility code (controller) from application logic code (model) and from code for displaying data (view) in applications with graphical user interface. With this approach we make the application better understandable, simplify future development and enable testing each unit of the application separately.
|
||||
|
||||
Please, [see documentation](https://doc.nette.org/en/presenters).
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Front Controller.
|
||||
*/
|
||||
class Application
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var int */
|
||||
public static $maxLoop = 20;
|
||||
|
||||
/** @var bool enable fault barrier? */
|
||||
public $catchExceptions;
|
||||
|
||||
/** @var string|null */
|
||||
public $errorPresenter;
|
||||
|
||||
/** @var callable[] function (Application $sender); Occurs before the application loads presenter */
|
||||
public $onStartup;
|
||||
|
||||
/** @var callable[] function (Application $sender, \Exception|\Throwable $e = null); Occurs before the application shuts down */
|
||||
public $onShutdown;
|
||||
|
||||
/** @var callable[] function (Application $sender, Request $request); Occurs when a new request is received */
|
||||
public $onRequest;
|
||||
|
||||
/** @var callable[] function (Application $sender, Presenter $presenter); Occurs when a presenter is created */
|
||||
public $onPresenter;
|
||||
|
||||
/** @var callable[] function (Application $sender, IResponse $response); Occurs when a new response is ready for dispatch */
|
||||
public $onResponse;
|
||||
|
||||
/** @var callable[] function (Application $sender, \Exception|\Throwable $e); Occurs when an unhandled exception occurs in the application */
|
||||
public $onError;
|
||||
|
||||
/** @var Request[] */
|
||||
private $requests = [];
|
||||
|
||||
/** @var IPresenter|null */
|
||||
private $presenter;
|
||||
|
||||
/** @var Nette\Http\IRequest */
|
||||
private $httpRequest;
|
||||
|
||||
/** @var Nette\Http\IResponse */
|
||||
private $httpResponse;
|
||||
|
||||
/** @var IPresenterFactory */
|
||||
private $presenterFactory;
|
||||
|
||||
/** @var IRouter */
|
||||
private $router;
|
||||
|
||||
|
||||
public function __construct(IPresenterFactory $presenterFactory, IRouter $router, Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
{
|
||||
$this->httpRequest = $httpRequest;
|
||||
$this->httpResponse = $httpResponse;
|
||||
$this->presenterFactory = $presenterFactory;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch a HTTP request to a front controller.
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
try {
|
||||
$this->onStartup($this);
|
||||
$this->processRequest($this->createInitialRequest());
|
||||
$this->onShutdown($this);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
if (isset($e)) {
|
||||
$this->onError($this, $e);
|
||||
if ($this->catchExceptions && $this->errorPresenter) {
|
||||
try {
|
||||
$this->processException($e);
|
||||
$this->onShutdown($this, $e);
|
||||
return;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->onError($this, $e);
|
||||
} catch (\Throwable $e) {
|
||||
$this->onError($this, $e);
|
||||
}
|
||||
}
|
||||
$this->onShutdown($this, $e);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
*/
|
||||
public function createInitialRequest()
|
||||
{
|
||||
$request = $this->router->match($this->httpRequest);
|
||||
if (!$request instanceof Request) {
|
||||
throw new BadRequestException('No route for HTTP request.');
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function processRequest(Request $request)
|
||||
{
|
||||
process:
|
||||
if (count($this->requests) > self::$maxLoop) {
|
||||
throw new ApplicationException('Too many loops detected in application life cycle.');
|
||||
}
|
||||
|
||||
$this->requests[] = $request;
|
||||
$this->onRequest($this, $request);
|
||||
|
||||
if (!$request->isMethod($request::FORWARD) && !strcasecmp($request->getPresenterName(), $this->errorPresenter)) {
|
||||
throw new BadRequestException('Invalid request. Presenter is not achievable.');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->presenter = $this->presenterFactory->createPresenter($request->getPresenterName());
|
||||
} catch (InvalidPresenterException $e) {
|
||||
throw count($this->requests) > 1 ? $e : new BadRequestException($e->getMessage(), 0, $e);
|
||||
}
|
||||
$this->onPresenter($this, $this->presenter);
|
||||
$response = $this->presenter->run(clone $request);
|
||||
|
||||
if ($response instanceof Responses\ForwardResponse) {
|
||||
$request = $response->getRequest();
|
||||
goto process;
|
||||
|
||||
} elseif ($response) {
|
||||
$this->onResponse($this, $response);
|
||||
$response->send($this->httpRequest, $this->httpResponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Exception|\Throwable
|
||||
* @return void
|
||||
*/
|
||||
public function processException($e)
|
||||
{
|
||||
if (!$e instanceof BadRequestException && $this->httpResponse instanceof Nette\Http\Response) {
|
||||
$this->httpResponse->warnOnBuffer = false;
|
||||
}
|
||||
if (!$this->httpResponse->isSent()) {
|
||||
$this->httpResponse->setCode($e instanceof BadRequestException ? ($e->getHttpCode() ?: 404) : 500);
|
||||
}
|
||||
|
||||
$args = ['exception' => $e, 'request' => end($this->requests) ?: null];
|
||||
if ($this->presenter instanceof UI\Presenter) {
|
||||
try {
|
||||
$this->presenter->forward(":$this->errorPresenter:", $args);
|
||||
} catch (AbortException $foo) {
|
||||
$this->processRequest($this->presenter->getLastCreatedRequest());
|
||||
}
|
||||
} else {
|
||||
$this->processRequest(new Request($this->errorPresenter, Request::FORWARD, $args));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all processed requests.
|
||||
* @return Request[]
|
||||
*/
|
||||
public function getRequests()
|
||||
{
|
||||
return $this->requests;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current presenter.
|
||||
* @return IPresenter|null
|
||||
*/
|
||||
public function getPresenter()
|
||||
{
|
||||
return $this->presenter;
|
||||
}
|
||||
|
||||
|
||||
/********************* services ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns router.
|
||||
* @return IRouter
|
||||
*/
|
||||
public function getRouter()
|
||||
{
|
||||
return $this->router;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns presenter factory.
|
||||
* @return IPresenterFactory
|
||||
*/
|
||||
public function getPresenterFactory()
|
||||
{
|
||||
return $this->presenterFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace NetteModule;
|
||||
|
||||
use Nette;
|
||||
use Nette\Application;
|
||||
use Nette\Http;
|
||||
use Tracy\ILogger;
|
||||
|
||||
|
||||
/**
|
||||
* Default Error Presenter.
|
||||
*/
|
||||
class ErrorPresenter implements Application\IPresenter
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var ILogger|null */
|
||||
private $logger;
|
||||
|
||||
|
||||
public function __construct(ILogger $logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Application\IResponse
|
||||
*/
|
||||
public function run(Application\Request $request)
|
||||
{
|
||||
$e = $request->getParameter('exception');
|
||||
if ($e instanceof Application\BadRequestException) {
|
||||
$code = $e->getHttpCode();
|
||||
} else {
|
||||
$code = 500;
|
||||
if ($this->logger) {
|
||||
$this->logger->log($e, ILogger::EXCEPTION);
|
||||
}
|
||||
}
|
||||
return new Application\Responses\CallbackResponse(function (Http\IRequest $httpRequest, Http\IResponse $httpResponse) use ($code) {
|
||||
if (preg_match('#^text/html(?:;|$)#', $httpResponse->getHeader('Content-Type'))) {
|
||||
require __DIR__ . '/templates/error.phtml';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Application helpers.
|
||||
*/
|
||||
class Helpers
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/**
|
||||
* Splits name into [module, presenter] or [presenter, action]
|
||||
* @return array
|
||||
*/
|
||||
public static function splitName($name)
|
||||
{
|
||||
$pos = strrpos($name, ':');
|
||||
return $pos === false
|
||||
? ['', $name, '']
|
||||
: [substr($name, 0, $pos), (string) substr($name, $pos + 1), ':'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
|
||||
/**
|
||||
* Presenter converts Request to IResponse.
|
||||
*/
|
||||
interface IPresenter
|
||||
{
|
||||
|
||||
/**
|
||||
* @return IResponse
|
||||
*/
|
||||
function run(Request $request);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
|
||||
/**
|
||||
* Responsible for creating a new instance of given presenter.
|
||||
*/
|
||||
interface IPresenterFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Generates and checks presenter class name.
|
||||
* @param string presenter name
|
||||
* @return string class name
|
||||
* @throws InvalidPresenterException
|
||||
*/
|
||||
function getPresenterClass(&$name);
|
||||
|
||||
/**
|
||||
* Creates new presenter instance.
|
||||
* @param string presenter name
|
||||
* @return IPresenter
|
||||
*/
|
||||
function createPresenter($name);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Any response returned by presenter.
|
||||
*/
|
||||
interface IResponse
|
||||
{
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* The bi-directional router.
|
||||
*/
|
||||
interface IRouter
|
||||
{
|
||||
/** only matching route */
|
||||
const ONE_WAY = 0b0001;
|
||||
|
||||
/** @deprecated */
|
||||
const SECURED = 0b0010;
|
||||
|
||||
/**
|
||||
* Maps HTTP request to a Request object.
|
||||
* @return Request|null
|
||||
*/
|
||||
function match(Nette\Http\IRequest $httpRequest);
|
||||
|
||||
/**
|
||||
* Constructs absolute URL from Request object.
|
||||
* @return string|null
|
||||
*/
|
||||
function constructUrl(Request $appRequest, Nette\Http\Url $refUrl);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Link generator.
|
||||
*/
|
||||
class LinkGenerator
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var IRouter */
|
||||
private $router;
|
||||
|
||||
/** @var Nette\Http\Url */
|
||||
private $refUrl;
|
||||
|
||||
/** @var IPresenterFactory|null */
|
||||
private $presenterFactory;
|
||||
|
||||
|
||||
public function __construct(IRouter $router, Nette\Http\Url $refUrl, IPresenterFactory $presenterFactory = null)
|
||||
{
|
||||
$this->router = $router;
|
||||
$this->refUrl = $refUrl;
|
||||
$this->presenterFactory = $presenterFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates URL to presenter.
|
||||
* @param string destination in format "[[[module:]presenter:]action] [#fragment]"
|
||||
* @return string
|
||||
* @throws UI\InvalidLinkException
|
||||
*/
|
||||
public function link($dest, array $params = [])
|
||||
{
|
||||
if (!preg_match('~^([\w:]+):(\w*+)(#.*)?()\z~', $dest, $m)) {
|
||||
throw new UI\InvalidLinkException("Invalid link destination '$dest'.");
|
||||
}
|
||||
list(, $presenter, $action, $frag) = $m;
|
||||
|
||||
try {
|
||||
$class = $this->presenterFactory ? $this->presenterFactory->getPresenterClass($presenter) : null;
|
||||
} catch (InvalidPresenterException $e) {
|
||||
throw new UI\InvalidLinkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
if (is_subclass_of($class, UI\Presenter::class)) {
|
||||
if ($action === '') {
|
||||
$action = UI\Presenter::DEFAULT_ACTION;
|
||||
}
|
||||
if (
|
||||
method_exists($class, $method = $class::formatActionMethod($action))
|
||||
|| method_exists($class, $method = $class::formatRenderMethod($action))
|
||||
) {
|
||||
UI\Presenter::argsToParams($class, $method, $params, [], $missing);
|
||||
if ($missing) {
|
||||
$rp = $missing[0];
|
||||
throw new UI\InvalidLinkException("Missing parameter \${$rp->getName()} required by {$rp->getDeclaringClass()->getName()}::{$rp->getDeclaringFunction()->getName()}()");
|
||||
}
|
||||
|
||||
} elseif (array_key_exists(0, $params)) {
|
||||
throw new UI\InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($action !== '') {
|
||||
$params[UI\Presenter::ACTION_KEY] = $action;
|
||||
}
|
||||
|
||||
$url = $this->router->constructUrl(new Request($presenter, null, $params), $this->refUrl);
|
||||
if ($url === null) {
|
||||
unset($params[UI\Presenter::ACTION_KEY]);
|
||||
$params = urldecode(http_build_query($params, null, ', '));
|
||||
throw new UI\InvalidLinkException("No route for $dest($params)");
|
||||
}
|
||||
return $url . $frag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace NetteModule;
|
||||
|
||||
use Latte;
|
||||
use Nette;
|
||||
use Nette\Application;
|
||||
use Nette\Application\Responses;
|
||||
use Nette\Http;
|
||||
|
||||
|
||||
/**
|
||||
* Micro presenter.
|
||||
*/
|
||||
class MicroPresenter implements Application\IPresenter
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var Nette\DI\Container|null */
|
||||
private $context;
|
||||
|
||||
/** @var Nette\Http\IRequest|null */
|
||||
private $httpRequest;
|
||||
|
||||
/** @var Application\IRouter|null */
|
||||
private $router;
|
||||
|
||||
/** @var Application\Request|null */
|
||||
private $request;
|
||||
|
||||
|
||||
public function __construct(Nette\DI\Container $context = null, Http\IRequest $httpRequest = null, Application\IRouter $router = null)
|
||||
{
|
||||
$this->context = $context;
|
||||
$this->httpRequest = $httpRequest;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the context.
|
||||
* @return Nette\DI\Container|null
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Nette\Application\IResponse
|
||||
*/
|
||||
public function run(Application\Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
if ($this->httpRequest && $this->router && !$this->httpRequest->isAjax() && ($request->isMethod('get') || $request->isMethod('head'))) {
|
||||
$refUrl = clone $this->httpRequest->getUrl();
|
||||
$url = $this->router->constructUrl($request, $refUrl->setPath($refUrl->getScriptPath()));
|
||||
if ($url !== null && !$this->httpRequest->getUrl()->isEqual($url)) {
|
||||
return new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY);
|
||||
}
|
||||
}
|
||||
|
||||
$params = $request->getParameters();
|
||||
if (!isset($params['callback'])) {
|
||||
throw new Application\BadRequestException('Parameter callback is missing.');
|
||||
}
|
||||
$callback = $params['callback'];
|
||||
$reflection = Nette\Utils\Callback::toReflection(Nette\Utils\Callback::check($callback));
|
||||
|
||||
if ($this->context) {
|
||||
foreach ($reflection->getParameters() as $param) {
|
||||
if ($param->getClass()) {
|
||||
$params[$param->getName()] = $this->context->getByType($param->getClass()->getName(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
$params['presenter'] = $this;
|
||||
$params = Application\UI\ComponentReflection::combineArgs($reflection, $params);
|
||||
|
||||
$response = call_user_func_array($callback, $params);
|
||||
|
||||
if (is_string($response)) {
|
||||
$response = [$response, []];
|
||||
}
|
||||
if (is_array($response)) {
|
||||
list($templateSource, $templateParams) = $response;
|
||||
$response = $this->createTemplate()->setParameters($templateParams);
|
||||
if (!$templateSource instanceof \SplFileInfo) {
|
||||
$response->getLatte()->setLoader(new Latte\Loaders\StringLoader);
|
||||
}
|
||||
$response->setFile($templateSource);
|
||||
}
|
||||
if ($response instanceof Application\UI\ITemplate) {
|
||||
return new Responses\TextResponse($response);
|
||||
} else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Template factory.
|
||||
* @param string
|
||||
* @return Application\UI\ITemplate
|
||||
*/
|
||||
public function createTemplate($class = null, callable $latteFactory = null)
|
||||
{
|
||||
$latte = $latteFactory ? $latteFactory() : $this->getContext()->getByType(Nette\Bridges\ApplicationLatte\ILatteFactory::class)->create();
|
||||
$template = $class ? new $class : new Nette\Bridges\ApplicationLatte\Template($latte);
|
||||
|
||||
$template->setParameters($this->request->getParameters());
|
||||
$template->presenter = $this;
|
||||
$template->context = $this->context;
|
||||
if ($this->httpRequest) {
|
||||
$url = $this->httpRequest->getUrl();
|
||||
$template->baseUrl = rtrim($url->getBaseUrl(), '/');
|
||||
$template->basePath = rtrim($url->getBasePath(), '/');
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Redirects to another URL.
|
||||
* @param string
|
||||
* @param int HTTP code
|
||||
* @return Nette\Application\Responses\RedirectResponse
|
||||
*/
|
||||
public function redirectUrl($url, $httpCode = Http\IResponse::S302_FOUND)
|
||||
{
|
||||
return new Responses\RedirectResponse($url, $httpCode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throws HTTP error.
|
||||
* @param string
|
||||
* @param int HTTP error code
|
||||
* @return void
|
||||
* @throws Nette\Application\BadRequestException
|
||||
*/
|
||||
public function error($message = null, $httpCode = Http\IResponse::S404_NOT_FOUND)
|
||||
{
|
||||
throw new Application\BadRequestException($message, $httpCode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Nette\Application\Request|null
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Default presenter loader.
|
||||
*/
|
||||
class PresenterFactory implements IPresenterFactory
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array[] of module => splited mask */
|
||||
private $mapping = [
|
||||
'*' => ['', '*Module\\', '*Presenter'],
|
||||
'Nette' => ['NetteModule\\', '*\\', '*Presenter'],
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
private $cache = [];
|
||||
|
||||
/** @var callable */
|
||||
private $factory;
|
||||
|
||||
|
||||
/**
|
||||
* @param callable function (string $class): IPresenter
|
||||
*/
|
||||
public function __construct(callable $factory = null)
|
||||
{
|
||||
$this->factory = $factory ?: function ($class) { return new $class; };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates new presenter instance.
|
||||
* @param string presenter name
|
||||
* @return IPresenter
|
||||
*/
|
||||
public function createPresenter($name)
|
||||
{
|
||||
return call_user_func($this->factory, $this->getPresenterClass($name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates and checks presenter class name.
|
||||
* @param string presenter name
|
||||
* @return string class name
|
||||
* @throws InvalidPresenterException
|
||||
*/
|
||||
public function getPresenterClass(&$name)
|
||||
{
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
}
|
||||
|
||||
if (!is_string($name) || !Nette\Utils\Strings::match($name, '#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*\z#')) {
|
||||
throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid.");
|
||||
}
|
||||
|
||||
$class = $this->formatPresenterClass($name);
|
||||
if (!class_exists($class)) {
|
||||
throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found.");
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$class = $reflection->getName();
|
||||
|
||||
if (!$reflection->implementsInterface(IPresenter::class)) {
|
||||
throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor.");
|
||||
} elseif ($reflection->isAbstract()) {
|
||||
throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract.");
|
||||
}
|
||||
|
||||
$this->cache[$name] = $class;
|
||||
|
||||
if ($name !== ($realName = $this->unformatPresenterClass($class))) {
|
||||
trigger_error("Case mismatch on presenter name '$name', correct name is '$realName'.", E_USER_WARNING);
|
||||
$name = $realName;
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets mapping as pairs [module => mask]
|
||||
* @return static
|
||||
*/
|
||||
public function setMapping(array $mapping)
|
||||
{
|
||||
foreach ($mapping as $module => $mask) {
|
||||
if (is_string($mask)) {
|
||||
if (!preg_match('#^\\\\?([\w\\\\]*\\\\)?(\w*\*\w*?\\\\)?([\w\\\\]*\*\w*)\z#', $mask, $m)) {
|
||||
throw new Nette\InvalidStateException("Invalid mapping mask '$mask'.");
|
||||
}
|
||||
$this->mapping[$module] = [$m[1], $m[2] ?: '*Module\\', $m[3]];
|
||||
} elseif (is_array($mask) && count($mask) === 3) {
|
||||
$this->mapping[$module] = [$mask[0] ? $mask[0] . '\\' : '', $mask[1] . '\\', $mask[2]];
|
||||
} else {
|
||||
throw new Nette\InvalidStateException("Invalid mapping mask for module $module.");
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats presenter class name from its name.
|
||||
* @param string
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function formatPresenterClass($presenter)
|
||||
{
|
||||
$parts = explode(':', $presenter);
|
||||
$mapping = isset($parts[1], $this->mapping[$parts[0]])
|
||||
? $this->mapping[array_shift($parts)]
|
||||
: $this->mapping['*'];
|
||||
|
||||
while ($part = array_shift($parts)) {
|
||||
$mapping[0] .= str_replace('*', $part, $mapping[$parts ? 1 : 2]);
|
||||
}
|
||||
return $mapping[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats presenter name from class name.
|
||||
* @param string
|
||||
* @return string|null
|
||||
* @internal
|
||||
*/
|
||||
public function unformatPresenterClass($class)
|
||||
{
|
||||
foreach ($this->mapping as $module => $mapping) {
|
||||
$mapping = str_replace(['\\', '*'], ['\\\\', '(\w+)'], $mapping);
|
||||
if (preg_match("#^\\\\?$mapping[0]((?:$mapping[1])*)$mapping[2]\\z#i", $class, $matches)) {
|
||||
return ($module === '*' ? '' : $module . ':')
|
||||
. preg_replace("#$mapping[1]#iA", '$1:', $matches[1]) . $matches[3];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Presenter request.
|
||||
*
|
||||
* @property string $presenterName
|
||||
* @property array $parameters
|
||||
* @property array $post
|
||||
* @property array $files
|
||||
* @property string|null $method
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** method */
|
||||
const FORWARD = 'FORWARD';
|
||||
|
||||
/** flag */
|
||||
const SECURED = 'secured';
|
||||
|
||||
/** flag */
|
||||
const RESTORED = 'restored';
|
||||
|
||||
/** flag */
|
||||
const VARYING = 'varying';
|
||||
|
||||
/** @var string|null */
|
||||
private $method;
|
||||
|
||||
/** @var array */
|
||||
private $flags = [];
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var array */
|
||||
private $params;
|
||||
|
||||
/** @var array */
|
||||
private $post;
|
||||
|
||||
/** @var array */
|
||||
private $files;
|
||||
|
||||
|
||||
/**
|
||||
* @param string fully qualified presenter name (module:module:presenter)
|
||||
* @param string method
|
||||
* @param array variables provided to the presenter usually via URL
|
||||
* @param array variables provided to the presenter via POST
|
||||
* @param array all uploaded files
|
||||
* @param array flags
|
||||
*/
|
||||
public function __construct($name, $method = null, array $params = [], array $post = [], array $files = [], array $flags = [])
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->method = $method;
|
||||
$this->params = $params;
|
||||
$this->post = $post;
|
||||
$this->files = $files;
|
||||
$this->flags = $flags;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the presenter name.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setPresenterName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the presenter name.
|
||||
* @return string
|
||||
*/
|
||||
public function getPresenterName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets variables provided to the presenter.
|
||||
* @return static
|
||||
*/
|
||||
public function setParameters(array $params)
|
||||
{
|
||||
$this->params = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all variables provided to the presenter (usually via URL).
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a parameter provided to the presenter.
|
||||
* @param string
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParameter($key)
|
||||
{
|
||||
return isset($this->params[$key]) ? $this->params[$key] : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets variables provided to the presenter via POST.
|
||||
* @return static
|
||||
*/
|
||||
public function setPost(array $params)
|
||||
{
|
||||
$this->post = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a variable provided to the presenter via POST.
|
||||
* If no key is passed, returns the entire array.
|
||||
* @param string
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPost($key = null)
|
||||
{
|
||||
if (func_num_args() === 0) {
|
||||
return $this->post;
|
||||
|
||||
} elseif (isset($this->post[$key])) {
|
||||
return $this->post[$key];
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets all uploaded files.
|
||||
* @return static
|
||||
*/
|
||||
public function setFiles(array $files)
|
||||
{
|
||||
$this->files = $files;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all uploaded files.
|
||||
* @return array
|
||||
*/
|
||||
public function getFiles()
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the method.
|
||||
* @param string|null
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod($method)
|
||||
{
|
||||
$this->method = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the method.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the method is the given one.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function isMethod($method)
|
||||
{
|
||||
return strcasecmp($this->method, $method) === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the flag.
|
||||
* @param string
|
||||
* @param bool
|
||||
* @return static
|
||||
*/
|
||||
public function setFlag($flag, $value = true)
|
||||
{
|
||||
$this->flags[$flag] = (bool) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the flag.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFlag($flag)
|
||||
{
|
||||
return !empty($this->flags[$flag]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Callback response.
|
||||
*/
|
||||
class CallbackResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
|
||||
/**
|
||||
* @param callable function (Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
*/
|
||||
public function __construct(callable $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
{
|
||||
call_user_func($this->callback, $httpRequest, $httpResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* File download response.
|
||||
*/
|
||||
class FileResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var bool */
|
||||
public $resuming = true;
|
||||
|
||||
/** @var string */
|
||||
private $file;
|
||||
|
||||
/** @var string */
|
||||
private $contentType;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var bool */
|
||||
private $forceDownload;
|
||||
|
||||
|
||||
/**
|
||||
* @param string file path
|
||||
* @param string imposed file name
|
||||
* @param string MIME content type
|
||||
*/
|
||||
public function __construct($file, $name = null, $contentType = null, $forceDownload = true)
|
||||
{
|
||||
if (!is_file($file)) {
|
||||
throw new Nette\Application\BadRequestException("File '$file' doesn't exist.");
|
||||
}
|
||||
|
||||
$this->file = $file;
|
||||
$this->name = $name ?: basename($file);
|
||||
$this->contentType = $contentType ?: 'application/octet-stream';
|
||||
$this->forceDownload = $forceDownload;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the path to a downloaded file.
|
||||
* @return string
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the MIME content type of a downloaded file.
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
{
|
||||
$httpResponse->setContentType($this->contentType);
|
||||
$httpResponse->setHeader('Content-Disposition',
|
||||
($this->forceDownload ? 'attachment' : 'inline')
|
||||
. '; filename="' . $this->name . '"'
|
||||
. '; filename*=utf-8\'\'' . rawurlencode($this->name));
|
||||
|
||||
$filesize = $length = filesize($this->file);
|
||||
$handle = fopen($this->file, 'r');
|
||||
|
||||
if ($this->resuming) {
|
||||
$httpResponse->setHeader('Accept-Ranges', 'bytes');
|
||||
if (preg_match('#^bytes=(\d*)-(\d*)\z#', $httpRequest->getHeader('Range'), $matches)) {
|
||||
list(, $start, $end) = $matches;
|
||||
if ($start === '') {
|
||||
$start = max(0, $filesize - $end);
|
||||
$end = $filesize - 1;
|
||||
|
||||
} elseif ($end === '' || $end > $filesize - 1) {
|
||||
$end = $filesize - 1;
|
||||
}
|
||||
if ($end < $start) {
|
||||
$httpResponse->setCode(416); // requested range not satisfiable
|
||||
return;
|
||||
}
|
||||
|
||||
$httpResponse->setCode(206);
|
||||
$httpResponse->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $filesize);
|
||||
$length = $end - $start + 1;
|
||||
fseek($handle, $start);
|
||||
|
||||
} else {
|
||||
$httpResponse->setHeader('Content-Range', 'bytes 0-' . ($filesize - 1) . '/' . $filesize);
|
||||
}
|
||||
}
|
||||
|
||||
$httpResponse->setHeader('Content-Length', $length);
|
||||
while (!feof($handle) && $length > 0) {
|
||||
echo $s = fread($handle, min(4000000, $length));
|
||||
$length -= strlen($s);
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Forwards to new request.
|
||||
*/
|
||||
class ForwardResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var Nette\Application\Request */
|
||||
private $request;
|
||||
|
||||
|
||||
public function __construct(Nette\Application\Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Nette\Application\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* JSON response used mainly for AJAX requests.
|
||||
*/
|
||||
class JsonResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var mixed */
|
||||
private $payload;
|
||||
|
||||
/** @var string */
|
||||
private $contentType;
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed payload
|
||||
* @param string MIME content type
|
||||
*/
|
||||
public function __construct($payload, $contentType = null)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
$this->contentType = $contentType ?: 'application/json';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPayload()
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the MIME content type of a downloaded file.
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
{
|
||||
$httpResponse->setContentType($this->contentType, 'utf-8');
|
||||
echo Nette\Utils\Json::encode($this->payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
use Nette\Http;
|
||||
|
||||
|
||||
/**
|
||||
* Redirects to new URI.
|
||||
*/
|
||||
class RedirectResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string */
|
||||
private $url;
|
||||
|
||||
/** @var int */
|
||||
private $httpCode;
|
||||
|
||||
|
||||
/**
|
||||
* @param string URI
|
||||
* @param int HTTP code 3xx
|
||||
*/
|
||||
public function __construct($url, $httpCode = Http\IResponse::S302_FOUND)
|
||||
{
|
||||
$this->url = (string) $url;
|
||||
$this->httpCode = (int) $httpCode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->httpCode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
public function send(Http\IRequest $httpRequest, Http\IResponse $httpResponse)
|
||||
{
|
||||
$httpResponse->redirect($this->url, $this->httpCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* String output response.
|
||||
*/
|
||||
class TextResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var mixed */
|
||||
private $source;
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed renderable variable
|
||||
*/
|
||||
public function __construct($source)
|
||||
{
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends response to output.
|
||||
* @return void
|
||||
*/
|
||||
public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse)
|
||||
{
|
||||
if ($this->source instanceof Nette\Application\UI\ITemplate) {
|
||||
$this->source->render();
|
||||
|
||||
} else {
|
||||
echo $this->source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Application\Responses;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* No response.
|
||||
*/
|
||||
final class VoidResponse implements Nette\Application\IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Routers;
|
||||
|
||||
use Nette;
|
||||
use Nette\Application;
|
||||
|
||||
|
||||
/**
|
||||
* The unidirectional router for CLI. (experimental)
|
||||
*/
|
||||
class CliRouter implements Application\IRouter
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
const PRESENTER_KEY = 'action';
|
||||
|
||||
/** @var array */
|
||||
private $defaults;
|
||||
|
||||
|
||||
/**
|
||||
* @param array default values
|
||||
*/
|
||||
public function __construct($defaults = [])
|
||||
{
|
||||
$this->defaults = $defaults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Maps command line arguments to a Request object.
|
||||
* @return Nette\Application\Request|null
|
||||
*/
|
||||
public function match(Nette\Http\IRequest $httpRequest)
|
||||
{
|
||||
if (empty($_SERVER['argv']) || !is_array($_SERVER['argv'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$names = [self::PRESENTER_KEY];
|
||||
$params = $this->defaults;
|
||||
$args = $_SERVER['argv'];
|
||||
array_shift($args);
|
||||
$args[] = '--';
|
||||
|
||||
foreach ($args as $arg) {
|
||||
$opt = preg_replace('#/|-+#A', '', $arg);
|
||||
if ($opt === $arg) {
|
||||
if (isset($flag) || $flag = array_shift($names)) {
|
||||
$params[$flag] = $arg;
|
||||
} else {
|
||||
$params[] = $arg;
|
||||
}
|
||||
$flag = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($flag)) {
|
||||
$params[$flag] = true;
|
||||
$flag = null;
|
||||
}
|
||||
|
||||
if ($opt !== '') {
|
||||
$pair = explode('=', $opt, 2);
|
||||
if (isset($pair[1])) {
|
||||
$params[$pair[0]] = $pair[1];
|
||||
} else {
|
||||
$flag = $pair[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($params[self::PRESENTER_KEY])) {
|
||||
throw new Nette\InvalidStateException('Missing presenter & action in route definition.');
|
||||
}
|
||||
list($module, $presenter) = Nette\Application\Helpers::splitName($params[self::PRESENTER_KEY]);
|
||||
if ($module !== '') {
|
||||
$params[self::PRESENTER_KEY] = $presenter;
|
||||
$presenter = $module;
|
||||
}
|
||||
|
||||
return new Application\Request(
|
||||
$presenter,
|
||||
'CLI',
|
||||
$params
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This router is only unidirectional.
|
||||
* @return void
|
||||
*/
|
||||
public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns default values.
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaults()
|
||||
{
|
||||
return $this->defaults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Routers;
|
||||
|
||||
use Nette;
|
||||
use Nette\Application;
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
|
||||
/**
|
||||
* The bidirectional route is responsible for mapping
|
||||
* HTTP request to a Request object for dispatch and vice-versa.
|
||||
*/
|
||||
class Route implements Application\IRouter
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
const PRESENTER_KEY = 'presenter';
|
||||
|
||||
const MODULE_KEY = 'module';
|
||||
|
||||
/** @internal url type */
|
||||
const HOST = 1,
|
||||
PATH = 2,
|
||||
RELATIVE = 3;
|
||||
|
||||
/** key used in {@link Route::$styles} or metadata {@link Route::__construct} */
|
||||
const VALUE = 'value';
|
||||
|
||||
const PATTERN = 'pattern';
|
||||
|
||||
const FILTER_IN = 'filterIn';
|
||||
|
||||
const FILTER_OUT = 'filterOut';
|
||||
|
||||
const FILTER_TABLE = 'filterTable';
|
||||
|
||||
const FILTER_STRICT = 'filterStrict';
|
||||
|
||||
/** @internal fixity types - how to handle default value? {@link Route::$metadata} */
|
||||
const OPTIONAL = 0,
|
||||
PATH_OPTIONAL = 1,
|
||||
CONSTANT = 2;
|
||||
|
||||
/** @deprecated */
|
||||
public static $defaultFlags = 0;
|
||||
|
||||
/** @var array */
|
||||
public static $styles = [
|
||||
'#' => [ // default style for path parameters
|
||||
self::PATTERN => '[^/]+',
|
||||
self::FILTER_OUT => [__CLASS__, 'param2path'],
|
||||
],
|
||||
'?#' => [// default style for query parameters
|
||||
],
|
||||
'module' => [
|
||||
self::PATTERN => '[a-z][a-z0-9.-]*',
|
||||
self::FILTER_IN => [__CLASS__, 'path2presenter'],
|
||||
self::FILTER_OUT => [__CLASS__, 'presenter2path'],
|
||||
],
|
||||
'presenter' => [
|
||||
self::PATTERN => '[a-z][a-z0-9.-]*',
|
||||
self::FILTER_IN => [__CLASS__, 'path2presenter'],
|
||||
self::FILTER_OUT => [__CLASS__, 'presenter2path'],
|
||||
],
|
||||
'action' => [
|
||||
self::PATTERN => '[a-z][a-z0-9-]*',
|
||||
self::FILTER_IN => [__CLASS__, 'path2action'],
|
||||
self::FILTER_OUT => [__CLASS__, 'action2path'],
|
||||
],
|
||||
'?module' => [
|
||||
],
|
||||
'?presenter' => [
|
||||
],
|
||||
'?action' => [
|
||||
],
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
private $mask;
|
||||
|
||||
/** @var array */
|
||||
private $sequence;
|
||||
|
||||
/** @var string regular expression pattern */
|
||||
private $re;
|
||||
|
||||
/** @var string[] parameter aliases in regular expression */
|
||||
private $aliases;
|
||||
|
||||
/** @var array of [value & fixity, filterIn, filterOut] */
|
||||
private $metadata = [];
|
||||
|
||||
/** @var array */
|
||||
private $xlat;
|
||||
|
||||
/** @var int HOST, PATH, RELATIVE */
|
||||
private $type;
|
||||
|
||||
/** @var string http | https */
|
||||
private $scheme;
|
||||
|
||||
/** @var int */
|
||||
private $flags;
|
||||
|
||||
/** @var Nette\Http\Url */
|
||||
private $lastRefUrl;
|
||||
|
||||
/** @var string */
|
||||
private $lastBaseUrl;
|
||||
|
||||
|
||||
/**
|
||||
* @param string URL mask, e.g. '<presenter>/<action>/<id \d{1,3}>'
|
||||
* @param array|string|\Closure default values or metadata or callback for NetteModule\MicroPresenter
|
||||
* @param int flags
|
||||
*/
|
||||
public function __construct($mask, $metadata = [], $flags = 0)
|
||||
{
|
||||
if (is_string($metadata)) {
|
||||
list($presenter, $action) = Nette\Application\Helpers::splitName($metadata);
|
||||
if (!$presenter) {
|
||||
throw new Nette\InvalidArgumentException("Second argument must be array or string in format Presenter:action, '$metadata' given.");
|
||||
}
|
||||
$metadata = [self::PRESENTER_KEY => $presenter];
|
||||
if ($action !== '') {
|
||||
$metadata['action'] = $action;
|
||||
}
|
||||
} elseif ($metadata instanceof \Closure || $metadata instanceof Nette\Callback) {
|
||||
if ($metadata instanceof Nette\Callback) {
|
||||
trigger_error('Nette\Callback is deprecated, use Nette\Utils\Callback::closure().', E_USER_DEPRECATED);
|
||||
}
|
||||
$metadata = [
|
||||
self::PRESENTER_KEY => 'Nette:Micro',
|
||||
'callback' => $metadata,
|
||||
];
|
||||
}
|
||||
|
||||
$this->flags = $flags | static::$defaultFlags;
|
||||
$this->setMask($mask, $metadata);
|
||||
if (static::$defaultFlags) {
|
||||
trigger_error('Route::$defaultFlags is deprecated, router by default keeps the used protocol.', E_USER_DEPRECATED);
|
||||
} elseif ($flags & self::SECURED) {
|
||||
trigger_error('Router::SECURED is deprecated, specify scheme in mask.', E_USER_DEPRECATED);
|
||||
$this->scheme = 'https';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Maps HTTP request to a Request object.
|
||||
* @return Nette\Application\Request|null
|
||||
*/
|
||||
public function match(Nette\Http\IRequest $httpRequest)
|
||||
{
|
||||
// combine with precedence: mask (params in URL-path), fixity, query, (post,) defaults
|
||||
|
||||
// 1) URL MASK
|
||||
$url = $httpRequest->getUrl();
|
||||
$re = $this->re;
|
||||
|
||||
if ($this->type === self::HOST) {
|
||||
$host = $url->getHost();
|
||||
$path = '//' . $host . $url->getPath();
|
||||
$parts = ip2long($host) ? [$host] : array_reverse(explode('.', $host));
|
||||
$re = strtr($re, [
|
||||
'/%basePath%/' => preg_quote($url->getBasePath(), '#'),
|
||||
'%tld%' => preg_quote($parts[0], '#'),
|
||||
'%domain%' => preg_quote(isset($parts[1]) ? "$parts[1].$parts[0]" : $parts[0], '#'),
|
||||
'%sld%' => preg_quote(isset($parts[1]) ? $parts[1] : '', '#'),
|
||||
'%host%' => preg_quote($host, '#'),
|
||||
]);
|
||||
|
||||
} elseif ($this->type === self::RELATIVE) {
|
||||
$basePath = $url->getBasePath();
|
||||
if (strncmp($url->getPath(), $basePath, strlen($basePath)) !== 0) {
|
||||
return null;
|
||||
}
|
||||
$path = (string) substr($url->getPath(), strlen($basePath));
|
||||
|
||||
} else {
|
||||
$path = $url->getPath();
|
||||
}
|
||||
|
||||
if ($path !== '') {
|
||||
$path = rtrim(rawurldecode($path), '/') . '/';
|
||||
}
|
||||
|
||||
if (!$matches = Strings::match($path, $re)) {
|
||||
// stop, not matched
|
||||
return null;
|
||||
}
|
||||
|
||||
// assigns matched values to parameters
|
||||
$params = [];
|
||||
foreach ($matches as $k => $v) {
|
||||
if (is_string($k) && $v !== '') {
|
||||
$params[$this->aliases[$k]] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2) CONSTANT FIXITY
|
||||
foreach ($this->metadata as $name => $meta) {
|
||||
if (!isset($params[$name]) && isset($meta['fixity']) && $meta['fixity'] !== self::OPTIONAL) {
|
||||
$params[$name] = null; // cannot be overwriten in 3) and detected by isset() in 4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3) QUERY
|
||||
if ($this->xlat) {
|
||||
$params += self::renameKeys($httpRequest->getQuery(), array_flip($this->xlat));
|
||||
} else {
|
||||
$params += $httpRequest->getQuery();
|
||||
}
|
||||
|
||||
|
||||
// 4) APPLY FILTERS & FIXITY
|
||||
foreach ($this->metadata as $name => $meta) {
|
||||
if (isset($params[$name])) {
|
||||
if (!is_scalar($params[$name])) {
|
||||
// do nothing
|
||||
} elseif (isset($meta[self::FILTER_TABLE][$params[$name]])) { // applies filterTable only to scalar parameters
|
||||
$params[$name] = $meta[self::FILTER_TABLE][$params[$name]];
|
||||
|
||||
} elseif (isset($meta[self::FILTER_TABLE]) && !empty($meta[self::FILTER_STRICT])) {
|
||||
return null; // rejected by filterTable
|
||||
|
||||
} elseif (isset($meta[self::FILTER_IN])) { // applies filterIn only to scalar parameters
|
||||
$params[$name] = call_user_func($meta[self::FILTER_IN], (string) $params[$name]);
|
||||
if ($params[$name] === null && !isset($meta['fixity'])) {
|
||||
return null; // rejected by filter
|
||||
}
|
||||
}
|
||||
|
||||
} elseif (isset($meta['fixity'])) {
|
||||
$params[$name] = $meta[self::VALUE];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->metadata[null][self::FILTER_IN])) {
|
||||
$params = call_user_func($this->metadata[null][self::FILTER_IN], $params);
|
||||
if ($params === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 5) BUILD Request
|
||||
if (!isset($params[self::PRESENTER_KEY])) {
|
||||
throw new Nette\InvalidStateException('Missing presenter in route definition.');
|
||||
} elseif (!is_string($params[self::PRESENTER_KEY])) {
|
||||
return null;
|
||||
}
|
||||
$presenter = $params[self::PRESENTER_KEY];
|
||||
unset($params[self::PRESENTER_KEY]);
|
||||
|
||||
if (isset($this->metadata[self::MODULE_KEY])) {
|
||||
$presenter = (isset($params[self::MODULE_KEY]) ? $params[self::MODULE_KEY] . ':' : '') . $presenter;
|
||||
unset($params[self::MODULE_KEY]);
|
||||
}
|
||||
|
||||
return new Application\Request(
|
||||
$presenter,
|
||||
$httpRequest->getMethod(),
|
||||
$params,
|
||||
$httpRequest->getPost(),
|
||||
$httpRequest->getFiles(),
|
||||
[Application\Request::SECURED => $httpRequest->isSecured()]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs absolute URL from Request object.
|
||||
* @return string|null
|
||||
*/
|
||||
public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl)
|
||||
{
|
||||
if ($this->flags & self::ONE_WAY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$params = $appRequest->getParameters();
|
||||
$metadata = $this->metadata;
|
||||
|
||||
$presenter = $appRequest->getPresenterName();
|
||||
$params[self::PRESENTER_KEY] = $presenter;
|
||||
|
||||
if (isset($metadata[self::MODULE_KEY])) { // try split into module and [submodule:]presenter parts
|
||||
$module = $metadata[self::MODULE_KEY];
|
||||
if (isset($module['fixity']) && strncmp($presenter, $module[self::VALUE] . ':', strlen($module[self::VALUE]) + 1) === 0) {
|
||||
$a = strlen($module[self::VALUE]);
|
||||
} else {
|
||||
$a = strrpos($presenter, ':');
|
||||
}
|
||||
if ($a === false) {
|
||||
$params[self::MODULE_KEY] = isset($module[self::VALUE]) ? '' : null;
|
||||
} else {
|
||||
$params[self::MODULE_KEY] = substr($presenter, 0, $a);
|
||||
$params[self::PRESENTER_KEY] = substr($presenter, $a + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($metadata[null][self::FILTER_OUT])) {
|
||||
$params = call_user_func($metadata[null][self::FILTER_OUT], $params);
|
||||
if ($params === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($metadata as $name => $meta) {
|
||||
if (!isset($params[$name])) {
|
||||
continue; // retains null values
|
||||
}
|
||||
|
||||
if (is_scalar($params[$name])) {
|
||||
$params[$name] = $params[$name] === false ? '0' : (string) $params[$name];
|
||||
}
|
||||
|
||||
if (isset($meta['fixity'])) {
|
||||
if ($params[$name] === $meta[self::VALUE]) { // remove default values; null values are retain
|
||||
unset($params[$name]);
|
||||
continue;
|
||||
|
||||
} elseif ($meta['fixity'] === self::CONSTANT) {
|
||||
return null; // missing or wrong parameter '$name'
|
||||
}
|
||||
}
|
||||
|
||||
if (is_scalar($params[$name]) && isset($meta['filterTable2'][$params[$name]])) {
|
||||
$params[$name] = $meta['filterTable2'][$params[$name]];
|
||||
|
||||
} elseif (isset($meta['filterTable2']) && !empty($meta[self::FILTER_STRICT])) {
|
||||
return null;
|
||||
|
||||
} elseif (isset($meta[self::FILTER_OUT])) {
|
||||
$params[$name] = call_user_func($meta[self::FILTER_OUT], $params[$name]);
|
||||
}
|
||||
|
||||
if (isset($meta[self::PATTERN]) && !preg_match($meta[self::PATTERN], rawurldecode($params[$name]))) {
|
||||
return null; // pattern not match
|
||||
}
|
||||
}
|
||||
|
||||
// compositing path
|
||||
$sequence = $this->sequence;
|
||||
$brackets = [];
|
||||
$required = null; // null for auto-optional
|
||||
$url = '';
|
||||
$i = count($sequence) - 1;
|
||||
do {
|
||||
$url = $sequence[$i] . $url;
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
$i--;
|
||||
|
||||
$name = $sequence[$i--]; // parameter name
|
||||
|
||||
if ($name === ']') { // opening optional part
|
||||
$brackets[] = $url;
|
||||
|
||||
} elseif ($name[0] === '[') { // closing optional part
|
||||
$tmp = array_pop($brackets);
|
||||
if ($required < count($brackets) + 1) { // is this level optional?
|
||||
if ($name !== '[!') { // and not "required"-optional
|
||||
$url = $tmp;
|
||||
}
|
||||
} else {
|
||||
$required = count($brackets);
|
||||
}
|
||||
|
||||
} elseif ($name[0] === '?') { // "foo" parameter
|
||||
continue;
|
||||
|
||||
} elseif (isset($params[$name]) && $params[$name] != '') { // intentionally ==
|
||||
$required = count($brackets); // make this level required
|
||||
$url = $params[$name] . $url;
|
||||
unset($params[$name]);
|
||||
|
||||
} elseif (isset($metadata[$name]['fixity'])) { // has default value?
|
||||
if ($required === null && !$brackets) { // auto-optional
|
||||
$url = '';
|
||||
} else {
|
||||
$url = $metadata[$name]['defOut'] . $url;
|
||||
}
|
||||
|
||||
} else {
|
||||
return null; // missing parameter '$name'
|
||||
}
|
||||
} while (true);
|
||||
|
||||
$scheme = $this->scheme ?: $refUrl->getScheme();
|
||||
|
||||
if ($this->type === self::HOST) {
|
||||
$host = $refUrl->getHost();
|
||||
$parts = ip2long($host) ? [$host] : array_reverse(explode('.', $host));
|
||||
$url = strtr($url, [
|
||||
'/%basePath%/' => $refUrl->getBasePath(),
|
||||
'%tld%' => $parts[0],
|
||||
'%domain%' => isset($parts[1]) ? "$parts[1].$parts[0]" : $parts[0],
|
||||
'%sld%' => isset($parts[1]) ? $parts[1] : '',
|
||||
'%host%' => $host,
|
||||
]);
|
||||
$url = $scheme . ':' . $url;
|
||||
} else {
|
||||
if ($this->lastRefUrl !== $refUrl) {
|
||||
$basePath = ($this->type === self::RELATIVE ? $refUrl->getBasePath() : '');
|
||||
$this->lastBaseUrl = $scheme . '://' . $refUrl->getAuthority() . $basePath;
|
||||
$this->lastRefUrl = $refUrl;
|
||||
}
|
||||
$url = $this->lastBaseUrl . $url;
|
||||
}
|
||||
|
||||
if (strpos($url, '//', strlen($scheme) + 3) !== false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// build query string
|
||||
if ($this->xlat) {
|
||||
$params = self::renameKeys($params, $this->xlat);
|
||||
}
|
||||
|
||||
$sep = ini_get('arg_separator.input');
|
||||
$query = http_build_query($params, '', $sep ? $sep[0] : '&');
|
||||
if ($query != '') { // intentionally ==
|
||||
$url .= '?' . $query;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse mask and array of default values; initializes object.
|
||||
* @param string
|
||||
* @param array
|
||||
* @return void
|
||||
*/
|
||||
private function setMask($mask, array $metadata)
|
||||
{
|
||||
$this->mask = $mask;
|
||||
|
||||
// detect '//host/path' vs. '/abs. path' vs. 'relative path'
|
||||
if (preg_match('#(?:(https?):)?(//.*)#A', $mask, $m)) {
|
||||
$this->type = self::HOST;
|
||||
list(, $this->scheme, $mask) = $m;
|
||||
|
||||
} elseif (substr($mask, 0, 1) === '/') {
|
||||
$this->type = self::PATH;
|
||||
|
||||
} else {
|
||||
$this->type = self::RELATIVE;
|
||||
}
|
||||
|
||||
foreach ($metadata as $name => $meta) {
|
||||
if (!is_array($meta)) {
|
||||
$metadata[$name] = $meta = [self::VALUE => $meta];
|
||||
}
|
||||
|
||||
if (array_key_exists(self::VALUE, $meta)) {
|
||||
if (is_scalar($meta[self::VALUE])) {
|
||||
$metadata[$name][self::VALUE] = $meta[self::VALUE] === false ? '0' : (string) $meta[self::VALUE];
|
||||
}
|
||||
$metadata[$name]['fixity'] = self::CONSTANT;
|
||||
}
|
||||
}
|
||||
|
||||
if (strpbrk($mask, '?<>[]') === false) {
|
||||
$this->re = '#' . preg_quote($mask, '#') . '/?\z#A';
|
||||
$this->sequence = [$mask];
|
||||
$this->metadata = $metadata;
|
||||
return;
|
||||
}
|
||||
|
||||
// PARSE MASK
|
||||
// <parameter-name[=default] [pattern]> or [ or ] or ?...
|
||||
$parts = Strings::split($mask, '/<([^<>= ]+)(=[^<> ]*)? *([^<>]*)>|(\[!?|\]|\s*\?.*)/');
|
||||
|
||||
$this->xlat = [];
|
||||
$i = count($parts) - 1;
|
||||
|
||||
// PARSE QUERY PART OF MASK
|
||||
if (isset($parts[$i - 1]) && substr(ltrim($parts[$i - 1]), 0, 1) === '?') {
|
||||
// name=<parameter-name [pattern]>
|
||||
$matches = Strings::matchAll($parts[$i - 1], '/(?:([a-zA-Z0-9_.-]+)=)?<([^> ]+) *([^>]*)>/');
|
||||
|
||||
foreach ($matches as list(, $param, $name, $pattern)) { // $pattern is not used
|
||||
if (isset(static::$styles['?' . $name])) {
|
||||
$meta = static::$styles['?' . $name];
|
||||
} else {
|
||||
$meta = static::$styles['?#'];
|
||||
}
|
||||
|
||||
if (isset($metadata[$name])) {
|
||||
$meta = $metadata[$name] + $meta;
|
||||
}
|
||||
|
||||
if (array_key_exists(self::VALUE, $meta)) {
|
||||
$meta['fixity'] = self::OPTIONAL;
|
||||
}
|
||||
|
||||
unset($meta['pattern']);
|
||||
$meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? null : array_flip($meta[self::FILTER_TABLE]);
|
||||
|
||||
$metadata[$name] = $meta;
|
||||
if ($param !== '') {
|
||||
$this->xlat[$name] = $param;
|
||||
}
|
||||
}
|
||||
$i -= 5;
|
||||
}
|
||||
|
||||
// PARSE PATH PART OF MASK
|
||||
$brackets = 0; // optional level
|
||||
$re = '';
|
||||
$sequence = [];
|
||||
$autoOptional = true;
|
||||
$aliases = [];
|
||||
do {
|
||||
$part = $parts[$i]; // part of path
|
||||
if (strpbrk($part, '<>') !== false) {
|
||||
throw new Nette\InvalidArgumentException("Unexpected '$part' in mask '$mask'.");
|
||||
}
|
||||
array_unshift($sequence, $part);
|
||||
$re = preg_quote($part, '#') . $re;
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
$i--;
|
||||
|
||||
$part = $parts[$i]; // [ or ]
|
||||
if ($part === '[' || $part === ']' || $part === '[!') {
|
||||
$brackets += $part[0] === '[' ? -1 : 1;
|
||||
if ($brackets < 0) {
|
||||
throw new Nette\InvalidArgumentException("Unexpected '$part' in mask '$mask'.");
|
||||
}
|
||||
array_unshift($sequence, $part);
|
||||
$re = ($part[0] === '[' ? '(?:' : ')?') . $re;
|
||||
$i -= 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
$pattern = trim($parts[$i--]); // validation condition (as regexp)
|
||||
$default = $parts[$i--]; // default value
|
||||
$name = $parts[$i--]; // parameter name
|
||||
array_unshift($sequence, $name);
|
||||
|
||||
if ($name[0] === '?') { // "foo" parameter
|
||||
$name = substr($name, 1);
|
||||
$re = $pattern ? '(?:' . preg_quote($name, '#') . "|$pattern)$re" : preg_quote($name, '#') . $re;
|
||||
$sequence[1] = $name . $sequence[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
// pattern, condition & metadata
|
||||
if (isset(static::$styles[$name])) {
|
||||
$meta = static::$styles[$name];
|
||||
} else {
|
||||
$meta = static::$styles['#'];
|
||||
}
|
||||
|
||||
if (isset($metadata[$name])) {
|
||||
$meta = $metadata[$name] + $meta;
|
||||
}
|
||||
|
||||
if ($pattern == '' && isset($meta[self::PATTERN])) {
|
||||
$pattern = $meta[self::PATTERN];
|
||||
}
|
||||
|
||||
if ($default !== '') {
|
||||
$meta[self::VALUE] = (string) substr($default, 1);
|
||||
$meta['fixity'] = self::PATH_OPTIONAL;
|
||||
}
|
||||
|
||||
$meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? null : array_flip($meta[self::FILTER_TABLE]);
|
||||
if (array_key_exists(self::VALUE, $meta)) {
|
||||
if (isset($meta['filterTable2'][$meta[self::VALUE]])) {
|
||||
$meta['defOut'] = $meta['filterTable2'][$meta[self::VALUE]];
|
||||
|
||||
} elseif (isset($meta[self::FILTER_OUT])) {
|
||||
$meta['defOut'] = call_user_func($meta[self::FILTER_OUT], $meta[self::VALUE]);
|
||||
|
||||
} else {
|
||||
$meta['defOut'] = $meta[self::VALUE];
|
||||
}
|
||||
}
|
||||
$meta[self::PATTERN] = "#(?:$pattern)\\z#A";
|
||||
|
||||
// include in expression
|
||||
$aliases['p' . $i] = $name;
|
||||
$re = '(?P<p' . $i . '>(?U)' . $pattern . ')' . $re;
|
||||
if ($brackets) { // is in brackets?
|
||||
if (!isset($meta[self::VALUE])) {
|
||||
$meta[self::VALUE] = $meta['defOut'] = null;
|
||||
}
|
||||
$meta['fixity'] = self::PATH_OPTIONAL;
|
||||
|
||||
} elseif (isset($meta['fixity'])) {
|
||||
if ($autoOptional) {
|
||||
$re = '(?:' . $re . ')?';
|
||||
}
|
||||
$meta['fixity'] = self::PATH_OPTIONAL;
|
||||
|
||||
} else {
|
||||
$autoOptional = false;
|
||||
}
|
||||
|
||||
$metadata[$name] = $meta;
|
||||
} while (true);
|
||||
|
||||
if ($brackets) {
|
||||
throw new Nette\InvalidArgumentException("Missing '[' in mask '$mask'.");
|
||||
}
|
||||
|
||||
$this->aliases = $aliases;
|
||||
$this->re = '#' . $re . '/?\z#A';
|
||||
$this->metadata = $metadata;
|
||||
$this->sequence = $sequence;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns mask.
|
||||
* @return string
|
||||
*/
|
||||
public function getMask()
|
||||
{
|
||||
return $this->mask;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns default values.
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaults()
|
||||
{
|
||||
$defaults = [];
|
||||
foreach ($this->metadata as $name => $meta) {
|
||||
if (isset($meta['fixity'])) {
|
||||
$defaults[$name] = $meta[self::VALUE];
|
||||
}
|
||||
}
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns flags.
|
||||
* @return int
|
||||
*/
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
|
||||
|
||||
/********************* Utilities ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Proprietary cache aim.
|
||||
* @internal
|
||||
* @return string[]|null
|
||||
*/
|
||||
public function getTargetPresenters()
|
||||
{
|
||||
if ($this->flags & self::ONE_WAY) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$m = $this->metadata;
|
||||
$module = '';
|
||||
|
||||
if (isset($m[self::MODULE_KEY])) {
|
||||
if (isset($m[self::MODULE_KEY]['fixity']) && $m[self::MODULE_KEY]['fixity'] === self::CONSTANT) {
|
||||
$module = $m[self::MODULE_KEY][self::VALUE] . ':';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($m[self::PRESENTER_KEY]['fixity']) && $m[self::PRESENTER_KEY]['fixity'] === self::CONSTANT) {
|
||||
return [$module . $m[self::PRESENTER_KEY][self::VALUE]];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rename keys in array.
|
||||
* @param array
|
||||
* @param array
|
||||
* @return array
|
||||
*/
|
||||
private static function renameKeys($arr, $xlat)
|
||||
{
|
||||
if (empty($xlat)) {
|
||||
return $arr;
|
||||
}
|
||||
|
||||
$res = [];
|
||||
$occupied = array_flip($xlat);
|
||||
foreach ($arr as $k => $v) {
|
||||
if (isset($xlat[$k])) {
|
||||
$res[$xlat[$k]] = $v;
|
||||
|
||||
} elseif (!isset($occupied[$k])) {
|
||||
$res[$k] = $v;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/********************* Inflectors ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* camelCaseAction name -> dash-separated.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public static function action2path($s)
|
||||
{
|
||||
$s = preg_replace('#(.)(?=[A-Z])#', '$1-', $s);
|
||||
$s = strtolower($s);
|
||||
$s = rawurlencode($s);
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dash-separated -> camelCaseAction name.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public static function path2action($s)
|
||||
{
|
||||
$s = preg_replace('#-(?=[a-z])#', ' ', $s);
|
||||
$s = lcfirst(ucwords($s));
|
||||
$s = str_replace(' ', '', $s);
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PascalCase:Presenter name -> dash-and-dot-separated.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public static function presenter2path($s)
|
||||
{
|
||||
$s = strtr($s, ':', '.');
|
||||
$s = preg_replace('#([^.])(?=[A-Z])#', '$1-', $s);
|
||||
$s = strtolower($s);
|
||||
$s = rawurlencode($s);
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dash-and-dot-separated -> PascalCase:Presenter name.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public static function path2presenter($s)
|
||||
{
|
||||
$s = preg_replace('#([.-])(?=[a-z])#', '$1 ', $s);
|
||||
$s = ucwords($s);
|
||||
$s = str_replace('. ', ':', $s);
|
||||
$s = str_replace('- ', '', $s);
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Url encode.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public static function param2path($s)
|
||||
{
|
||||
return str_replace('%2F', '/', rawurlencode($s));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Routers;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* The router broker.
|
||||
*/
|
||||
class RouteList extends Nette\Utils\ArrayList implements Nette\Application\IRouter
|
||||
{
|
||||
/** @var array */
|
||||
private $cachedRoutes;
|
||||
|
||||
/** @var string|null */
|
||||
private $module;
|
||||
|
||||
|
||||
public function __construct($module = null)
|
||||
{
|
||||
$this->module = $module ? $module . ':' : '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Maps HTTP request to a Request object.
|
||||
* @return Nette\Application\Request|null
|
||||
*/
|
||||
public function match(Nette\Http\IRequest $httpRequest)
|
||||
{
|
||||
foreach ($this as $route) {
|
||||
$appRequest = $route->match($httpRequest);
|
||||
if ($appRequest !== null) {
|
||||
$name = $appRequest->getPresenterName();
|
||||
if (strncmp($name, 'Nette:', 6)) {
|
||||
$appRequest->setPresenterName($this->module . $name);
|
||||
}
|
||||
return $appRequest;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs absolute URL from Request object.
|
||||
* @return string|null
|
||||
*/
|
||||
public function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl)
|
||||
{
|
||||
if ($this->cachedRoutes === null) {
|
||||
$this->warmupCache();
|
||||
}
|
||||
|
||||
if ($this->module) {
|
||||
if (strncmp($tmp = $appRequest->getPresenterName(), $this->module, strlen($this->module)) === 0) {
|
||||
$appRequest = clone $appRequest;
|
||||
$appRequest->setPresenterName(substr($tmp, strlen($this->module)));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$presenter = $appRequest->getPresenterName();
|
||||
if (!isset($this->cachedRoutes[$presenter])) {
|
||||
$presenter = '*';
|
||||
}
|
||||
|
||||
foreach ($this->cachedRoutes[$presenter] as $route) {
|
||||
$url = $route->constructUrl($appRequest, $refUrl);
|
||||
if ($url !== null) {
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function warmupCache()
|
||||
{
|
||||
$routes = [];
|
||||
$routes['*'] = [];
|
||||
|
||||
foreach ($this as $route) {
|
||||
$presenters = $route instanceof Route && is_array($tmp = $route->getTargetPresenters())
|
||||
? $tmp
|
||||
: array_keys($routes);
|
||||
|
||||
foreach ($presenters as $presenter) {
|
||||
if (!isset($routes[$presenter])) {
|
||||
$routes[$presenter] = $routes['*'];
|
||||
}
|
||||
$routes[$presenter][] = $route;
|
||||
}
|
||||
}
|
||||
|
||||
$this->cachedRoutes = $routes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the router.
|
||||
* @param mixed
|
||||
* @param Nette\Application\IRouter
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($index, $route)
|
||||
{
|
||||
if (!$route instanceof Nette\Application\IRouter) {
|
||||
throw new Nette\InvalidArgumentException('Argument must be IRouter descendant.');
|
||||
}
|
||||
parent::offsetSet($index, $route);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getModule()
|
||||
{
|
||||
return $this->module;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Application\Routers;
|
||||
|
||||
use Nette;
|
||||
use Nette\Application;
|
||||
|
||||
|
||||
/**
|
||||
* The bidirectional route for trivial routing via query parameters.
|
||||
*/
|
||||
class SimpleRouter implements Application\IRouter
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
const PRESENTER_KEY = 'presenter';
|
||||
|
||||
const MODULE_KEY = 'module';
|
||||
|
||||
/** @var string */
|
||||
private $module = '';
|
||||
|
||||
/** @var array */
|
||||
private $defaults;
|
||||
|
||||
/** @var int */
|
||||
private $flags;
|
||||
|
||||
|
||||
/**
|
||||
* @param array default values
|
||||
* @param int flags
|
||||
*/
|
||||
public function __construct($defaults = [], $flags = 0)
|
||||
{
|
||||
if (is_string($defaults)) {
|
||||
list($presenter, $action) = Nette\Application\Helpers::splitName($defaults);
|
||||
if (!$presenter) {
|
||||
throw new Nette\InvalidArgumentException("Argument must be array or string in format Presenter:action, '$defaults' given.");
|
||||
}
|
||||
$defaults = [
|
||||
self::PRESENTER_KEY => $presenter,
|
||||
'action' => $action === '' ? Application\UI\Presenter::DEFAULT_ACTION : $action,
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($defaults[self::MODULE_KEY])) {
|
||||
$this->module = $defaults[self::MODULE_KEY] . ':';
|
||||
unset($defaults[self::MODULE_KEY]);
|
||||
}
|
||||
|
||||
$this->defaults = $defaults;
|
||||
$this->flags = $flags;
|
||||
if ($flags & self::SECURED) {
|
||||
trigger_error('IRouter::SECURED is deprecated, router by default keeps the used protocol.', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Maps HTTP request to a Request object.
|
||||
* @return Nette\Application\Request|null
|
||||
*/
|
||||
public function match(Nette\Http\IRequest $httpRequest)
|
||||
{
|
||||
if ($httpRequest->getUrl()->getPathInfo() !== '') {
|
||||
return null;
|
||||
}
|
||||
// combine with precedence: get, (post,) defaults
|
||||
$params = $httpRequest->getQuery();
|
||||
$params += $this->defaults;
|
||||
|
||||
if (!isset($params[self::PRESENTER_KEY]) || !is_string($params[self::PRESENTER_KEY])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$presenter = $this->module . $params[self::PRESENTER_KEY];
|
||||
unset($params[self::PRESENTER_KEY]);
|
||||
|
||||
return new Application\Request(
|
||||
$presenter,
|
||||
$httpRequest->getMethod(),
|
||||
$params,
|
||||
$httpRequest->getPost(),
|
||||
$httpRequest->getFiles(),
|
||||
[Application\Request::SECURED => $httpRequest->isSecured()]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs absolute URL from Request object.
|
||||
* @return string|null
|
||||
*/
|
||||
public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl)
|
||||
{
|
||||
if ($this->flags & self::ONE_WAY) {
|
||||
return null;
|
||||
}
|
||||
$params = $appRequest->getParameters();
|
||||
|
||||
// presenter name
|
||||
$presenter = $appRequest->getPresenterName();
|
||||
if (strncmp($presenter, $this->module, strlen($this->module)) === 0) {
|
||||
$params[self::PRESENTER_KEY] = substr($presenter, strlen($this->module));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove default values; null values are retain
|
||||
foreach ($this->defaults as $key => $value) {
|
||||
if (isset($params[$key]) && $params[$key] == $value) { // intentionally ==
|
||||
unset($params[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$url = ($this->flags & self::SECURED ? 'https://' : $refUrl->getScheme() . '://') . $refUrl->getAuthority() . $refUrl->getPath();
|
||||
$sep = ini_get('arg_separator.input');
|
||||
$query = http_build_query($params, '', $sep ? $sep[0] : '&');
|
||||
if ($query != '') { // intentionally ==
|
||||
$url .= '?' . $query;
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns default values.
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaults()
|
||||
{
|
||||
return $this->defaults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns flags.
|
||||
* @return int
|
||||
*/
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user