diff --git a/.gitignore b/.gitignore index 3e759b7..5b14845 100644 --- a/.gitignore +++ b/.gitignore @@ -193,6 +193,8 @@ PublishScripts/ csx/ *.build.csdef +._* + # Microsoft Azure Emulator ecf/ rcf/ diff --git a/README.md b/README.md index e0d315f..8509fe6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,385 @@ # phacil-framework A super easy PHP Framework for web development! + +## Requirements + + - PHP 5.3+ + - OPCache PHP Extension + - HTTP Web Server (Apache 2.4+ recomended) + +## Structure + +| Folder | Description | +| ------ | ------ | +| Controller | Contains the structure and files of controllers code | +| Model | Contais directories and files for the model code | +| View | Contains the template files | +| Cache | Full writable default cache storage | +| public_html | Contains the index.php and .htaccess Apache file. All your access public files stay here, like CSS, JavaScript, etc. | +| System | Most important folder of this framework. Contains the libraries, classes and anothers features to improve correct working for the Web App. | +| Logs | Contais debug and error logs | +| config.php | File with contains all basic configurations for the Web App, like DB connection, mail config, directory config, etc.| + +## Get started! + +Copy or clone this repository in your computer/server and edit the config.php file. See below the Config File Basic Options. + +### Hello World! sample + +This is a simple Hello World! for this framework. + +1. In **controller** folder, create a folder called *common* and create a file called *home.php*. +2. Edit **controller/common/home.php** like that. + + ```php + data['variable'] = "Hello World!"; + + $this->out(); + } + } + ``` + +3. Now create a folder insede view called **default** and a subfolder called **common** and a file home.twig. +4. Edit the **view/default/common/home.twig** like this. + + ```html +

{{ variable }}

+ ``` +5. Edit the constants *HTTP_URL* and *DIR_APPLICATION* in file **config.php**. Edit others *DIR_** constants path if necessary. + ```php + ' - ExacTI phacil', + 'config_mail_protocol'=>'smtp', + 'config_error_display' => 1, + 'config_template' => "default", + 'config_error_filename'=> 'error.log'); + +//App folders +define('DIR_APPLICATION', '/Applications/MAMP/htdocs/phacil/'); +define('DIR_LOGS', DIR_APPLICATION.'logs/'); +define('DIR_PUBLIC', DIR_APPLICATION.'public_html/'); +define('DIR_SYSTEM', DIR_APPLICATION.'system/'); +define('DIR_IMAGE', DIR_APPLICATION.'public_html/imagens/'); +define('DIR_TEMPLATE', DIR_APPLICATION.'view/'); +define('DIR_CACHE', DIR_APPLICATION.'cache/'); + + + ``` +6. Access your web app in your favorite browser and enjoy this magnific Hello World! + +#### Explanation + +All routes are a mapping class with extends the primary Controller class. In a simple term, the class name in controller is *Controller****Folder****Filename*. + + `class ControllerFolderFile extends Controller { + }` + + The public function named `index()` is a default function to generate the page. + + `public function index() { }` + + Array `$this->data` receives in array format the variables to send to template, in this sample called **variable** with the string *Hello World!*. + + In the last we finish with a simple `$this->out();` to indicate the output and process template file. It's a automated mechanism to link this controller with the respective viewer. + + The viewer in this sample is a twig template format ([Twig page](https://twig.symfony.com)). ExacTI Phacil Framework permits to use varius PHP template engines, like Twig, Mustache, Dwoo and Smarty. + + ## Config file Parameters + + A simple description of default constants configurations in *config.php* file. + + | Constant | Type | Description | Required | + | ----- | ----- | ----- | ----- | + | HTTP_URL | string | Contains the URL for this WEB APP | Yes | + | HTTPS_URL | string | Same this HTTP_URL, but in SSL format | + | HTTP_IMAGE | string | The image URL | + | CDN | string | CDN URL to use for static content | + | DB_CONFIG | boolean | Permit to use configurations direct in database. Requires database instalattion. Values: true or false. | Yes | + | DEBUG | boolean | Indicate the debug mode. | Yes | + | `$configs` | array | No-SQL configs | + | DIR_APPLICATION | string | Absolute path to application folder | Yes | + | DIR_LOGS | string | Path to log folder | + | DIR_PUBLIC | string | Path to the public folder. This directory is a directory to configure in your HTTP server. | Yes | + | DIR_SYSTEM | string | System directory | Yes | + | DIR_IMAGE | string | Directory to store images used by Image library. | + | DIR_TEMPLATE | string | Directory with templates folder | Yes | + | DIR_CACHE | string | Directory to storage the generated caches. | Yes | + | DB_DRIVER | string | Driver to connect a database source | + | DB_HOSTNAME | string | Database host | + | DB_USERNAME | string | Username to connect a database | + | DB_PASSWORD | string | Database password | + | DB_DATABASE | string | Database name | + | SQL_CACHE | boolean | Use the SQL Select cache system | + + ## Template Engines Support + + This framework supports this PHP templates engines: + - TPL (basic template with PHP and HTML); + - [Twig](https://twig.symfony.com); + - [Mustache](https://mustache.github.io); + - [Dwoo](http://dwoo.org); + - [Smarty](https://www.smarty.net). + + To use a determined template engine, just create a file with name of engine in extension, for sample, if you like to use a Twig, the template file is **demo.twig**, if desire the Mustache template, use **demo.mustache** extension file. + The ExacTI Phacil Framework allows to use varius template engines in the same project. + + ## Easy made functions + + This framework is very focused in agile, secutiry and reutilizable code in PHP. Contains a very utily functions we see in the next section. + + ### Database + + To execute a query: + ```php + $variable = $this->db->query("SELECT * FROM mundo"); + ``` + + Withou SQL Cache (if enabled): + ```php + $variable = $this->db->query("SELECT * FROM mundo", false); + ``` +Escape values to more security (no SQL injection issue): + `$this->db->escape($value)` + +To get this rows: + `$variable->rows`; + + Get one row: `$variable->row`; + + Number of rows: `$variable->num_rows`; + + Sample: + ```php + db->query("SELECT * FROM settings WHERE code = '". $this->db->escape($code). "'"); + + $this->data['rows'] = $variable->rows; + $this->data['totalRecords'] = $variable->num_rows; + + return $this->data; + } + } + ``` + + ### Document easy made + + Use the especial Document class to manipulate easily informations about your final HTML. + + To call a document class, use `$this->document` inside Controller. + + To add a CSS: `$this->document->addStyle($href, $rel = 'stylesheet', $media = 'screen', $minify = true)`; + + To add a JavaScript: `$this->document->addScript($script, $sort = '0', $minify = true)`; + +Facebook Metatag: `$this->document->addFBMeta($property, $content = '')`; + +Document HTML Title: `$this->document->setTitle($title)`; + +Page description: `$this->document->setDescription($description)`; + + Sample: + ```php + document->addStyle('pipoca.css'); + } + } + ``` + +### Classes and functions + +This framework have a lot of utilities and accepts much more in system folder with autoload format. + +#### To show all classes + +Execute a Classes() class in one Controller. + + ```php + $cla = new Classes(); + +var_dump($cla->classes()); + ``` +#### To show all classes and functions registereds + + Execute a Classes() class and call functions() in one Controller. + + ```php + $cla = new Classes('HTML'); + +var_dump($cla->functions()); + ``` + + **Note:** *The HTML parameter is the output in HTML style. Is optional.* + + +## Loaders + +In this framework, loaders is a simple way to get resources to use in your PHP code. Is very intuitive and requirew few steps. + +For sample, to load a model, is just `$this->load->model('folder/file');` and to use is `$this->model_folder_file->object();`, like this sample: + ```php + load->model('data/json'); + $this->data['totalData'] = $this->model_data_json->total(); + + $this->out(); + } + } + + ``` + +You can use loaders to: + - Controllers; + - Models; + - Librarys; + - Configs; + - Databases; + - Languages. + +## Models + +This framework is totally MVC (Model, View and Controller) based. The models is just like the controllers and uses the same structure, with a different folder. + +To create a model, put in the models folder a directory and file with the code. + ```php + request` method. For sample, to obtain a POST value, use `$this->request->post['field']` to get the post value with security. + For a \$_SERVER predefined variables, use `$this->request->server['VALUE']` and $this->request->get() for \$_GET values. + The advantages to use this requests instead the predefined variables of PHP is more the more security, upgradable and unicode values. + + ### Sessions + + Sessions is a little different method, you can define and use with `$this->session->data['name']`. + +## Special controller parameters + +| Parameter | Type | Description| +| ----- | ----- | ----- | +| `$this->template` | string | Specify manualy the path to template | +| `$this->twig` | array | Create Twig aditional functions. Eg.: `$this->twig = array('base64' => function($str) { return base64_encode($str); }); `| +| `$this->children` | array | Load in variable other renders of web app. Th childrens *footer* and *header* is default loaded when uses `$this->out()` output. To load without this defaults childrens, use `$this->out(false)`.| +| `$this->redirect($url)` | string | Create a header to redirect to another page. | + + + + + +## Routes + +The ExacTI Phacil Framework is a simple router base to create a simple and consistent web navigation. + +Withou SEO URL, we invoke a page with *route* get parameter when contains a scheme folder/file of controller, like this: *http://example.com/index.php?route=folder/file*. + +In a sample case, we have this controller: + ```php + foo($this->request->get['p']); + } + private function foo($parameter) { + return ($parameter != 0) ? "another" : "other"; + } + } + ``` + + If we access *index.php?route=folder/file* we see the "Index" message. But, like a tree, if we access *index.php?route=folder/file/another* obtains the another function code, with "other" message. + + Multiple Get parameters is simple and we can define normaly, eg.: *index.php?route=folder/file/another&p=2* to print "another" message. + + Private and protected functions is only for internal use, if we tried to access *index.php?route=folder/file/foo*, the framework return 404 HTTP error. + + ### SEO URL + + If you need a beautiful URL for SEO or other optimizations, you can use the url_alias database table. Is very simple and "translate" the routes in URL. + + Imagine this SQL table called url_alias: + + | url_alias_id | query | get | keyword | + | ----- | ----- | ----- | ----- | + | 1 | contact/contato | | contact| + | 2 | webservice/sitemap | | sitemap | + + With the url_alias, the access to route *http://example.com/index.php?route=contact/contato* and *http://example.com/contact* is the same!(and very prety!). + + ### Links + + We have a function to create a strict and dinamic links automatic: `$this->url->link($route, $args = '', $connection = 'NONSSL')`. Is a simple function to generate internal links with correct URL encode. + + In sample of this table above, if we have this code: + ```php + echo $this->url->link('contact/contato'); + ``` + + We get this URL in output: *http://example.com/contact* + + But if you don't have a route in url_alias table, returns the complete route. + + ```php + echo $this->url->link('catalog/products'); + ``` + + Return this URL in output: *http://example.com/index.php?route=catalog/products* + + With extra GET paramethers: + ```php + $fields = array('foo' => 'bar'); + echo $this->url->link('contact/contato', $fields); + echo $this->url->link('contact/contato/place', $fields); + ``` + + We get this URL in output with GET paramethers: + + *http://example.com/contact?foo=bar* + + *http://example.com/index.php?route=contact/contato/place&foo=bar* + + ## License + + This project is manteined for [ExacTI IT Solutions](https://exacti.com.br) with GPLv3 license. \ No newline at end of file diff --git a/config.php b/config.php new file mode 100644 index 0000000..037428b --- /dev/null +++ b/config.php @@ -0,0 +1,42 @@ +' - ExacTI phacil', + 'config_mail_protocol'=>'smtp', + 'config_error_display' => 1, + 'config_template' => "default", + 'config_error_filename'=> 'error.log'); + +//App folders +define('DIR_APPLICATION', '/Applications/MAMP/htdocs/phacil/'); +define('DIR_LOGS', DIR_APPLICATION.'logs/'); +define('DIR_PUBLIC', DIR_APPLICATION.'public_html/'); +define('DIR_SYSTEM', DIR_APPLICATION.'system/'); +define('DIR_IMAGE', DIR_APPLICATION.'public_html/imagens/'); +define('DIR_TEMPLATE', DIR_APPLICATION.'view/'); +define('DIR_CACHE', DIR_APPLICATION.'cache/'); + +//Database Connection +/*define('DB_DRIVER', ''); +define('DB_HOSTNAME', ''); +define('DB_USERNAME', ''); +define('DB_PASSWORD', ''); +define('DB_DATABASE', ''); +define('SQL_CACHE', true);*/ + +//Cache definitions +define('CACHE_JS_CSS', true); +//define('CACHE_use_js_plus', false); +define('CACHE_MINIFY', true); +define('CACHE_DAYS', 15); + +define('CACHE_IMG', true); +define('USE_IMAGICK', true); +define('CACHE_STATIC_HTML', true); diff --git a/controller/common/footer.php b/controller/common/footer.php new file mode 100644 index 0000000..f3a818e --- /dev/null +++ b/controller/common/footer.php @@ -0,0 +1,14 @@ +config->get('config_template') . '/common/footer.twig')) { + $this->template = $this->config->get('config_template') . '/common/footer.twig'; + } else { + $this->template = 'default/common/footer.twig'; + } + + $this->render(); + + } +} diff --git a/controller/common/header.php b/controller/common/header.php new file mode 100644 index 0000000..555caa5 --- /dev/null +++ b/controller/common/header.php @@ -0,0 +1,34 @@ +data['title'] = $this->document->getTitle(); + + $this->document->addScript('https://code.jquery.com/jquery-3.3.1.min.js'); + $this->document->addScript('https://code.jquery.com/jquery-migrate-3.0.1.min.js'); + + $this->document->addScript('https://use.fontawesome.com/releases/v5.4.1/js/all.js'); + $this->document->addScript('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js'); + $this->document->addScript('https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js'); + + + + $this->document->addStyle('https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css'); + $this->document->addStyle('https://use.fontawesome.com/releases/v5.6.0/css/all.css'); + + $this->document->addStyle('assets/style.css'); + + $this->data['description'] = $this->document->getDescription(); + $this->data['keywords'] = $this->document->getKeywords(); + $this->data['links'] = $this->document->getLinks(); + $this->data['styles'] = $this->document->getStyles(); + $this->data['scripts'] = $this->document->getScripts(); + $this->data['google_analytics'] = html_entity_decode($this->config->get('config_google_analytics'), ENT_QUOTES, 'UTF-8'); + + $this->data['icon'] = HTTP_IMAGE.'favicon.png'; + + $this->template = 'default/common/header.twig'; + + $this->render(); + } +} +?> diff --git a/controller/common/home.php b/controller/common/home.php new file mode 100644 index 0000000..5c8a160 --- /dev/null +++ b/controller/common/home.php @@ -0,0 +1,11 @@ +document->setTitle("Hello World! %s"); + + $this->data['variable'] = "Hello World!"; + + $this->out(); + } +} \ No newline at end of file diff --git a/controller/error/not_found.php b/controller/error/not_found.php new file mode 100644 index 0000000..717241a --- /dev/null +++ b/controller/error/not_found.php @@ -0,0 +1,23 @@ +document->setTitle('404 %s'); + + header("HTTP/1.1 404 Not Found"); + header("Status: 404 Not Found"); + + $this->children = array( + 'common/footer', + 'common/header' + ); + + if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/error/not_found.tpl')) { + $this->template = $this->config->get('config_template') . '/error/not_found.tpl'; + } else { + $this->template = 'default/error/not_found.tpl'; + } + + $this->response->setOutput($this->render()); + } +} diff --git a/public_html/.htaccess b/public_html/.htaccess new file mode 100644 index 0000000..430e3ec --- /dev/null +++ b/public_html/.htaccess @@ -0,0 +1,94 @@ +AddType application/font-woff woff +AddType application/x-httpd-php esp +ExpiresActive on +ExpiresByType image/png "access plus 7 days" +ExpiresByType image/gif "access plus 7 days" +ExpiresByType image/jpeg "access plus 7 days" +# woff font type +ExpiresByType application/font-woff "access plus 7 days" +# css may change a bit sometimes +ExpiresByType text/css "access plus 7 days" +# special MIME type for icons +AddType image/vnd.microsoft.icon .ico +AddType application/x-shockwave-flash .swf +# now we have icon MIME type, we can use it +# my favicon doesn't change much +ExpiresByType image/vnd.microsoft.icon "access plus 1 months" +# js may change a bit sometimes +ExpiresByType text/js "access plus 7 days" +ExpiresByType application/x-javascript "access plus 7 days" +ExpiresByType application/x-shockwave-flash "access plus 7 days" +AddOutputFilterByType DEFLATE application/x-font-woff application/font-woff application/vnd.ms-fontobject application/x-font-ttf image/svg+xml + +Options +FollowSymlinks + +# Prevent Directoy listing + +Options -Indexes + + + Order deny,allow + Deny from all + + + + Header set Access-Control-Allow-Origin "*" + + + + RewriteEngine On + RewriteBase / + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA] + #RewriteRule ^([^?]*) /url/url.php?p=$1 [L,QSA] + + + + + Options +FollowSymlinks + RewriteEngine On + + # Adaptive-Images ----------------------------------------------------------------------------------- + + # Add any directories you wish to omit from the Adaptive-Images process on a new line, as follows: + # RewriteCond %{REQUEST_URI} !ignore-this-directory + # RewriteCond %{REQUEST_URI} !and-ignore-this-directory-too + + #RewriteCond %{REQUEST_URI} !assets + + # don't apply the AI behaviour to images inside AI's cache folder: + RewriteCond %{REQUEST_URI} !ai-cache + # don't apply the AI behaviour to images inside cache folder: + RewriteCond %{REQUEST_URI} !css-cache + + # Send any GIF, JPG, or PNG request that IS NOT stored inside one of the above directories + # to adaptive-images.php so we can select appropriately sized versions + + RewriteRule \.(?:jpe?g|png)$ index.php?_type_=img [L,QSA] + + # END Adaptive-Images ------------------------------------------------------------------------------- + + + # CSS --------------------------------------------------------------------------------- + + # Add any directories you wish to omit from the Adaptive-Images process on a new line, as follows: + # RewriteCond %{REQUEST_URI} !ignore-this-directory + # RewriteCond %{REQUEST_URI} !and-ignore-this-directory-too + + #RewriteCond %{REQUEST_URI} !assets + + # don't apply the AI behaviour to images inside AI's cache folder: + RewriteCond %{REQUEST_URI} !ai-cache + + # don't apply the AI behaviour to images inside cache folder: + RewriteCond %{REQUEST_URI} !css-cache + + # Send any CSS or JS request that IS NOT stored inside one of the above directories + # to adaptive-images.php so we can select appropriately sized versions + + #RewriteRule \.(?:css|js)$ index.php?_type_=script + + # END Adaptive-Images ------------------------------------------------------------------------------- + + diff --git a/public_html/favicon.ico b/public_html/favicon.ico new file mode 100644 index 0000000..7e0922d Binary files /dev/null and b/public_html/favicon.ico differ diff --git a/public_html/index.php b/public_html/index.php new file mode 100644 index 0000000..c5f535c --- /dev/null +++ b/public_html/index.php @@ -0,0 +1,21 @@ +'; + + $i = 0; + + foreach($l as $montado) { + + $i = $i; + + if($montado == end($l)) { + $bread .= '
  • '.$montado.'
  • '; + } else { + if($l[0] == 'Home' and $l[0] == $montado) { + $montado = ''; + } + $bread .= '
  • '.$montado.'
  • '; + + } $i = $i+1; + } + + $bread .= '
  • Voltar
  • +'; + + return $bread; + +} \ No newline at end of file diff --git a/system/captcha/12.gdf b/system/captcha/12.gdf new file mode 100644 index 0000000..8b72ece Binary files /dev/null and b/system/captcha/12.gdf differ diff --git a/system/captcha/13.gdf b/system/captcha/13.gdf new file mode 100644 index 0000000..75d6708 Binary files /dev/null and b/system/captcha/13.gdf differ diff --git a/system/captcha/14.gdf b/system/captcha/14.gdf new file mode 100644 index 0000000..b010779 Binary files /dev/null and b/system/captcha/14.gdf differ diff --git a/system/captcha/15.gdf b/system/captcha/15.gdf new file mode 100644 index 0000000..a01d7f7 Binary files /dev/null and b/system/captcha/15.gdf differ diff --git a/system/captcha/16.gdf b/system/captcha/16.gdf new file mode 100644 index 0000000..95e0e75 Binary files /dev/null and b/system/captcha/16.gdf differ diff --git a/system/captcha/17.gdf b/system/captcha/17.gdf new file mode 100644 index 0000000..2328c84 Binary files /dev/null and b/system/captcha/17.gdf differ diff --git a/system/captcha/18.gdf b/system/captcha/18.gdf new file mode 100644 index 0000000..d864c93 Binary files /dev/null and b/system/captcha/18.gdf differ diff --git a/system/captcha/autoload.php b/system/captcha/autoload.php new file mode 100644 index 0000000..3169d49 --- /dev/null +++ b/system/captcha/autoload.php @@ -0,0 +1,207 @@ +numChar = $numChar; + $this->background = $background; + $pos = 'ABCDEFGHJKLMNOPQRSTUWVXZ0123456789abcdefhijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUWVXZ0123456789'; + + for($i = 0; $i < $this->numChar; $i++) { + $this->code .= substr($pos, mt_rand(0, strlen($pos)-1), 1); + } + /*if (function_exists('token')) { + $this->code = substr(token(100), rand(0, 94), $this->numChar); + } else { + $this->code = substr(sha1(mt_rand()), 17, $this->numChar); + }*/ + + $this->width = ($width != NULL) ? $width : $this->width; + $this->height = ($height != NULL) ? $height : $this->height; + } + + function getCode(){ + return $this->code; + } + + function showImage($format = 'png') { + $image = imagecreatetruecolor($this->width, $this->height); + + $width = imagesx($image); + $height = imagesy($image); + + $black = imagecolorallocate($image, 33, 33, 33); + $white = imagecolorallocate($image, 255, 255, 255); + $red = imagecolorallocatealpha($image, 255, 0, 0, 50); + $green = imagecolorallocatealpha($image, 0, 255, 0, 75); + $blue = imagecolorallocatealpha($image, 0, 0, 255, 50); + $orange = imagecolorallocatealpha($image, 255, 136, 0, 50); + $yellow = imagecolorallocatealpha($image, 255, 255, 0, 50); + $punyWhite = imagecolorallocatealpha($image, 255, 255, 255, 40); + $varYellow = imagecolorallocatealpha($image, 255, 255, 0, rand(30,100)); + $varBlue = imagecolorallocatealpha($image, 0, 0, 255, rand(30,100)); + $varBlack = imagecolorallocatealpha($image, 33, 33, 33, rand(85,95)); + $pureYellow = imagecolorallocate($image, 255, 255, 0); + $pureGreen = imagecolorallocate($image, 0, 255, 0); + $softGreen = imagecolorallocate($image, 153, 241, 197); + $softBlue = imagecolorallocate($image, 180, 225, 245); + $softpink = imagecolorallocate($image, 250, 165, 215); + $pureRed = imagecolorallocate($image, 250, 0, 0); + $strongGreen = imagecolorallocate($image, 95, 115, 75); + $pureBlue = imagecolorallocate($image, 0, 0, 215); + $pureorange = imagecolorallocate($image, 255, 135, 0); + $strangePurple = imagecolorallocate($image, 0, 80, 90); + /*$pureBlue = imagecolorallocate($image, 200, 100, 245);*/ + + switch($this->background) { + case 'black': + $fontColors = array($white, $pureYellow, $pureGreen, $softBlue, $softGreen, $softpink); + imagefilledrectangle($image, 0, 0, $width, $height, $black); + break; + case 'white': + $fontColors = array($black, $pureRed, $strongGreen, $pureBlue, $pureorange, $strangePurple); + imagefilledrectangle($image, 0, 0, $width, $height, $white); + break; + } + + + if(rand(0,2) == 2) { + imagefilledellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $red); + } else { + imagefilledrectangle($image, ceil(rand(5, $this->width)), ceil(rand(5, $this->height)), ceil(rand(5, $this->width)), ceil(rand(5, $this->height)), $red); + } + if(rand(1,2) == 2) { + imagefilledellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $green); + } else { + imagefilledrectangle($image, ceil(rand(5, 145)), ceil(rand(0, 35)), ceil(rand(5, 175)), ceil(rand(0, 40)), $green); + } + if(rand(1,2) == 2) { + imagefilledellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $varBlue); + } else { + imagefilledrectangle($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $varBlue); + } + if(rand(1,2) == 2) { + imagefilledellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $orange); + } else { + imagefilledrectangle($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $orange); + } + if(rand(1,2) == 2) { + imagefilledellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $yellow); + } else { + imagefilledrectangle($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $yellow); + } + + + imagefilledrectangle($image, 0, 0, $width, 0, $black); + imagefilledrectangle($image, $width - 1, 0, $width - 1, $height - 1, $black); + imagefilledrectangle($image, 0, 0, 0, $height - 1, $black); + imagefilledrectangle($image, 0, $height - 1, $width, $height - 1, $black); + + $qualfonte = __DIR__."/18.gdf"; + + //Carregar uma nova fonte + $fonteCaptcha = imageloadfont($qualfonte); + + //imagestring($image, $fonteCaptcha, intval(($width - (strlen($this->code) * 6)) / 16), intval(($height - 15) / 4), $this->code, $white); + + $txt = str_split($this->code); + $space = ($this->width-10) / $this->numChar; + + foreach($txt as $key => $character) { + $y = ceil(rand(0, $this->height - ($this->height - ($this->height -30)))); + $divisor = 1; + $plus = 10; + $incre = 0; + switch ($key) { + case "0": + $x = ceil(rand(0, $space-$plus)); + break; + case "1": + $x = ceil(rand($x+$incre/$divisor+$plus, $space*2)); + break; + case "2": + $x = ceil(rand($x+$incre/$divisor+$plus, $space*3)); + break; + case "3": + $x = ceil(rand($x+$incre/$divisor+$plus, $space*4)); + break; + case "4": + $x = ceil(rand($x+$incre/$divisor+$plus, $space*5)); + break; + case "5": + $x = ceil(rand($x+$incre/$divisor+$plus, $space*5+$space/2)); + break; + default: + $x = ceil(rand($x+$incre/$divisor+$plus, $space*$key+$space/2)); + break; + } + + imagechar ( $image , $fonteCaptcha , $x , $y, $character , $fontColors[rand(0,count($fontColors)-1)]); + } + + + if(rand(1,2) == 2) { + imageellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $red); + } else { + imagerectangle($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $red); + } + if(rand(1,2) == 2) { + imageellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $green); + } else { + imageline($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $green); + } + if(rand(1,2) == 2) { + imageellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $blue); + } else { + imagerectangle($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $blue); + } + if(rand(1,2) == 2) { + imageellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $orange); + } else { + imageline($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $orange); + } + if(rand(1,2) == 2) { + imageellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $varYellow); + } else { + imageline($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $varYellow); + } + if(rand(1,2) == 2) { + imageellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $punyWhite); + } else { + imageline($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $punyWhite); + } + if(rand(1,2) == 2) { + imagefilledellipse($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), 30, 30, $varBlack); + } else { + imagefilledrectangle($image, ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), ceil(rand(5, $this->width)), ceil(rand(0, $this->height)), $varBlack); + } + //imagearc ( $image , ceil(rand(5, $this->width)) , ceil(rand(0, $this->height)) ,ceil(rand(5, $this->width)) , ceil(rand(0, $this->height)), ceil(rand(5, $this->width))/ceil(rand(5, $this->height)) , ceil(rand(5, $this->height))/ceil(rand(0, $this->width)) , $pureYellow ); + + header('Cache-Control: no-cache'); + + if($format == 'jpeg' || $format == 'jpg') { + header('Content-type: image/jpeg'); + imagejpeg($image); + } elseif ($format == 'png') { + header('Content-type: image/png'); + imagepng($image); + } elseif ($format == 'bmp' || $format == 'bitmap') { + header('Content-type: image/bmp'); + imagebmp($image); + } elseif ($format == 'gif' || $format == 'giff') { + header('Content-type: image/gif'); + imagegif($image); + } elseif ($format == 'wbmp') { + header('Content-type: image/vnd.wap.wbmp'); + imagewbmp($image); + } + + imagedestroy($image); + } +} +?> \ No newline at end of file diff --git a/system/captcha/knuckle.gdf b/system/captcha/knuckle.gdf new file mode 100644 index 0000000..9aed530 Binary files /dev/null and b/system/captcha/knuckle.gdf differ diff --git a/system/config/autoload.php b/system/config/autoload.php new file mode 100644 index 0000000..26591c6 --- /dev/null +++ b/system/config/autoload.php @@ -0,0 +1,32 @@ +data[$key]) ? $this->data[$key] : null); + } + + public function set($key, $value) { + $this->data[$key] = $value; + } + + public function has($key) { + return isset($this->data[$key]); + } + + public function load($filename) { + $file = DIR_CONFIG . $filename . '.php'; + + if (file_exists($file)) { + $cfg = array(); + + require($file); + + $this->data = array_merge($this->data, $cfg); + } else { + trigger_error('Error: Could not load config ' . $filename . '!'); + exit(); + } + } +} +?> \ No newline at end of file diff --git a/system/database/autoload.php b/system/database/autoload.php new file mode 100644 index 0000000..5052e80 --- /dev/null +++ b/system/database/autoload.php @@ -0,0 +1,15 @@ + PDO::ERRMODE_SILENT + ); + /** + * The number of rows affected by the last operation + * + * @var int + */ + private $affectedRows = 0; + /** + * The data for the database connection + * + * @var \stdClass + */ + private $params = array(); + /** + * Sets the connection and connects to the database + * + * @param string $host server Address + * @param string $user Username + * @param string $pass Password + * @param string $name The database name + * @param string $charset Encoding connection + */ + public function __construct($host, $user, $pass, $name, $charset = 'utf8mb4') + { + $this->params = new stdClass; + # keep connection data + $this->params->host = $host; + $this->params->user = $user; + $this->params->pass = $pass; + $this->params->name = $name; + $this->params->charset = $charset; + $this->params->connstr = "mysql:host={$host};dbname={$name};charset={$charset}"; + # add the connection parameters + $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES '{$charset}'"; + $this->connect(); + } + /** + * Connect to database + */ + public function connect() + { + try { + $this->dbh = new PDO($this->params->connstr, $this->params->user, $this->params->pass, $this->options); + if (version_compare(PHP_VERSION, '5.3.6', '<=')) { + $this->dbh->exec($this->options['PDO::MYSQL_ATTR_INIT_COMMAND']); + } + } catch (PDOException $exception) { + trigger_error($exception->getMessage()); + } + } + /** + * Query the database + * + * @param string $sql + * @return \stdClass + */ + public function query($sql = null) + { + if ($this->dbh) { + $data = new stdClass; + + $sth=$this->dbh->prepare($sql); + $sth->execute(); + //$sth= $this->dbh->query($sql); + $this->affectedRows = $sth->rowCount(); + $data->rows = $sth ? $sth->fetchAll() : array(); + $data->row = isset($data->rows[0]) ? $data->rows[0] : null; + $data->num_rows = $this->affectedRows; + return $data; + } + return null; + } + /** + * Concludes the string in quotation marks to be used in the query + * + * @param mixed $string shielded line + * @return string Returns shielded line or to FALSE , if the driver does not support screening + */ + public function escape($string = null) + { + return $this->dbh ? str_replace("'", "", $this->dbh->quote($string)) : null; + } + /** + * Gets the number of rows affected by the last operation + * + * @return int + */ + public function countAffected() + { + return $this->affectedRows; + } + /** + * Gets the ID of the last inserted row or sequence of values + * + * @return int + */ + public function getLastId() + { + return $this->dbh ? $this->dbh->lastInsertId() : 0; + } + /** + * Gets the name of the driver + * + * @return string|null + */ + public function getDriverName() + { + return $this->dbh ? $this->dbh->getAttribute(PDO::ATTR_DRIVER_NAME) : null; + } + /** + * Get information about the version of the client libraries that are used by the PDO driver + * + * @return string|null + */ + public function getVersion() + { + return $this->dbh ? $this->dbh->getAttribute(PDO::ATTR_CLIENT_VERSION) : null; + } + /** + * Closing a database connection + */ + public function close() + { + $this->dbh = null; + } + public function __destruct() + { + $this->close(); + } +} \ No newline at end of file diff --git a/system/database/database/dbmysqli.php b/system/database/database/dbmysqli.php new file mode 100644 index 0000000..e15ee26 --- /dev/null +++ b/system/database/database/dbmysqli.php @@ -0,0 +1,52 @@ +connection = new \mysqli($hostname, $username, $password, $database, $port); + if ($this->connection->connect_error) { + throw new \Exception('Error: ' . $this->connection->error . '
    Error No: ' . $this->connection->errno); + } + $this->connection->set_charset($charset); + $this->connection->query("SET SQL_MODE = ''"); + } + public function query($sql) { + $query = $this->connection->query($sql); + if (!$this->connection->errno) { + if ($query instanceof \mysqli_result) { + $data = array(); + while ($row = $query->fetch_assoc()) { + $data[] = $row; + } + $result = new \stdClass(); + $result->num_rows = $query->num_rows; + $result->row = isset($data[0]) ? $data[0] : array(); + $result->rows = $data; + $query->close(); + return $result; + } else { + return true; + } + } else { + throw new \Exception('Error: ' . $this->connection->error . '
    Error No: ' . $this->connection->errno . '
    ' . $sql); + } + } + public function escape($value) { + return $this->`->real_escape_string($value); + } + + public function countAffected() { + return $this->connection->affected_rows; + } + public function getLastId() { + return $this->connection->insert_id; + } + + public function isConnected() { + return $this->connection->ping(); + } + + public function __destruct() { + $this->connection->close(); + } +} +?> \ No newline at end of file diff --git a/system/database/database/mmsql.php b/system/database/database/mmsql.php new file mode 100644 index 0000000..acf0da0 --- /dev/null +++ b/system/database/database/mmsql.php @@ -0,0 +1,79 @@ +connection = mssql_connect($hostname, $username, $password)) { + exit('Error: Could not make a database connection using ' . $username . '@' . $hostname); + } + + if (!mssql_select_db($database, $this->connection)) { + exit('Error: Could not connect to database ' . $database); + } + + mssql_query("SET NAMES 'utf8'", $this->connection); + mssql_query("SET CHARACTER SET utf8", $this->connection); + mssql_query("SET CHARACTER_SET_CONNECTION=utf8", $this->connection); + } + + public function query($sql) { + $resource = mssql_query($sql, $this->connection); + + if ($resource) { + if (is_resource($resource)) { + $i = 0; + + $data = array(); + + while ($result = mssql_fetch_assoc($resource)) { + $data[$i] = $result; + + $i++; + } + + mssql_free_result($resource); + + $query = new stdClass(); + $query->row = isset($data[0]) ? $data[0] : array(); + $query->rows = $data; + $query->num_rows = $i; + + unset($data); + + return $query; + } else { + return true; + } + } else { + trigger_error('Error: ' . mssql_get_last_message($this->connection) . '
    ' . $sql); + exit(); + } + } + + public function escape($value) { + return mssql_real_escape_string($value, $this->connection); + } + + public function countAffected() { + return mssql_rows_affected($this->connection); + } + + public function getLastId() { + $last_id = false; + + $resource = mssql_query("SELECT @@identity AS id", $this->connection); + + if ($row = mssql_fetch_row($resource)) { + $last_id = trim($row[0]); + } + + mssql_free_result($resource); + + return $last_id; + } + + public function __destruct() { + mssql_close($this->connection); + } +} +?> \ No newline at end of file diff --git a/system/database/database/mpdo.php b/system/database/database/mpdo.php new file mode 100644 index 0000000..0bd1ab6 --- /dev/null +++ b/system/database/database/mpdo.php @@ -0,0 +1,110 @@ + true, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION); + + $this->connection = new \PDO($dsn, $username, $password, $options); + } catch(\PDOException $e) { + throw new \Exception('Failed to connect to database. Reason: \'' . $e->getMessage() . '\''); + } + $this->connection->exec("SET NAMES 'utf8'"); + $this->connection->exec("SET CHARACTER SET utf8"); + $this->connection->exec("SET CHARACTER_SET_CONNECTION=utf8"); + $this->connection->exec("SET SQL_MODE = ''"); + $this->rowCount = 0; + } + public function prepare($sql) { + $this->statement = $this->connection->prepare($sql); + } + public function bindParam($parameter, $variable, $data_type = \PDO::PARAM_STR, $length = 0) { + if ($length) { + $this->statement->bindParam($parameter, $variable, $data_type, $length); + } else { + $this->statement->bindParam($parameter, $variable, $data_type); + } + } + public function execute() { + try { + if ($this->statement && $this->statement->execute()) { + $data = array(); + while ($row = $this->statement->fetch(\PDO::FETCH_ASSOC)) { + $data[] = $row; + } + $result = new \stdClass(); + $result->row = (isset($data[0])) ? $data[0] : array(); + $result->rows = $data; + $result->num_rows = $this->statement->rowCount(); + } + } catch(\PDOException $e) { + throw new \Exception('Error: ' . $e->getMessage() . ' Error Code : ' . $e->getCode()); + } + } + public function query($sql, $params = array()) { + $this->statement = $this->connection->prepare($sql); + + $result = false; + try { + if ($this->statement && $this->statement->execute($params)) { + $data = array(); + $this->rowCount = $this->statement->rowCount(); + if($this->rowCount > 0) + { + try { + $data = $this->statement->fetchAll(\PDO::FETCH_ASSOC); + } + catch(\Exception $ex){} + } + // free up resources + $this->statement->closeCursor(); + $this->statement = null; + $result = new \stdClass(); + $result->row = (isset($data[0]) ? $data[0] : array()); + $result->rows = $data; + $result->num_rows = $this->rowCount; + } + } catch (\PDOException $e) { + throw new \Exception('Error: ' . $e->getMessage() . ' Error Code : ' . $e->getCode() . '
    ' . $sql); + } + if ($result) { + return $result; + } else { + $result = new \stdClass(); + $result->row = array(); + $result->rows = array(); + $result->num_rows = 0; + return $result; + } + } + public function escape($value) { + return str_replace(array("\\", "\0", "\n", "\r", "\x1a", "'", '"'), array("\\\\", "\\0", "\\n", "\\r", "\Z", "\'", '\"'), $value); + } + public function countAffected() { + if ($this->statement) { + return $this->statement->rowCount(); + } else { + return $this->rowCount; + } + } + public function getLastId() { + return $this->connection->lastInsertId(); + } + + public function isConnected() { + if ($this->connection) { + return true; + } else { + return false; + } + } + + public function __destruct() { + $this->connection = null; + } +} \ No newline at end of file diff --git a/system/database/database/mysql.php b/system/database/database/mysql.php new file mode 100644 index 0000000..bc4449f --- /dev/null +++ b/system/database/database/mysql.php @@ -0,0 +1,70 @@ +connection = mysql_connect($hostname, $username, $password)) { + exit('Error: Could not make a database connection using ' . $username . '@' . $hostname); + } + + if (!mysql_select_db($database, $this->connection)) { + exit('Error: Could not connect to database ' . $database); + } + + mysql_query("SET NAMES '".$charset."'", $this->connection); + mysql_query("SET CHARACTER SET ".$charset."", $this->connection); + mysql_query("SET CHARACTER_SET_CONNECTION=".$charset."", $this->connection); + mysql_query("SET SQL_MODE = ''", $this->connection); + } + + public function query($sql) { + $resource = mysql_query($sql, $this->connection); + + if ($resource) { + if (is_resource($resource)) { + $i = 0; + + $data = array(); + + while ($result = mysql_fetch_assoc($resource)) { + $data[$i] = $result; + + $i++; + } + + mysql_free_result($resource); + + $query = new stdClass(); + $query->row = isset($data[0]) ? $data[0] : array(); + $query->rows = $data; + $query->num_rows = $i; + + unset($data); + + return $query; + } else { + return true; + } + } else { + trigger_error('Error: ' . mysql_error($this->connection) . '
    Error No: ' . mysql_errno($this->connection) . '
    ' . $sql); + exit(); + } + } + + public function escape($value) { + return mysql_real_escape_string($value, $this->connection); + } + + public function countAffected() { + return mysql_affected_rows($this->connection); + } + + public function getLastId() { + return mysql_insert_id($this->connection); + } + + public function __destruct() { + mysql_close($this->connection); + } +} +?> \ No newline at end of file diff --git a/system/database/database/odbc.php b/system/database/database/odbc.php new file mode 100644 index 0000000..e69de29 diff --git a/system/database/database/postgre.php b/system/database/database/postgre.php new file mode 100644 index 0000000..777788e --- /dev/null +++ b/system/database/database/postgre.php @@ -0,0 +1,50 @@ +link = pg_connect('host=' . $hostname . ' port=' . $port . ' user=' . $username . ' password=' . $password . ' dbname=' . $database)) { + throw new \Exception('Error: Could not make a database link using ' . $username . '@' . $hostname); + } + if (!pg_ping($this->link)) { + throw new \Exception('Error: Could not connect to database ' . $database); + } + pg_query($this->link, "SET CLIENT_ENCODING TO 'UTF8'"); + } + public function query($sql) { + $resource = pg_query($this->link, $sql); + if ($resource) { + if (is_resource($resource)) { + $i = 0; + $data = array(); + while ($result = pg_fetch_assoc($resource)) { + $data[$i] = $result; + $i++; + } + pg_free_result($resource); + $query = new \stdClass(); + $query->row = isset($data[0]) ? $data[0] : array(); + $query->rows = $data; + $query->num_rows = $i; + unset($data); + return $query; + } else { + return true; + } + } else { + throw new \Exception('Error: ' . pg_result_error($this->link) . '
    ' . $sql); + } + } + public function escape($value) { + return pg_escape_string($this->link, $value); + } + public function countAffected() { + return pg_affected_rows($this->link); + } + public function getLastId() { + $query = $this->query("SELECT LASTVAL() AS `id`"); + return $query->row['id']; + } + public function __destruct() { + pg_close($this->link); + } +} \ No newline at end of file diff --git a/system/database/database/sqlite.php b/system/database/database/sqlite.php new file mode 100644 index 0000000..e69de29 diff --git a/system/database/library/db.php b/system/database/library/db.php new file mode 100644 index 0000000..9c003dc --- /dev/null +++ b/system/database/library/db.php @@ -0,0 +1,65 @@ +driver = new $driver($hostname, $username, $password, $database); + } + + public function query($sql, $cacheUse = true) { + + if(defined('SQL_CACHE') && SQL_CACHE == true && $cacheUse == true) { + + return $this->Cache($sql); + + } else { + + return $this->driver->query($sql); + } + + } + + public function escape($value) { + return $this->driver->escape($value); + } + + public function countAffected() { + return $this->driver->countAffected(); + } + + public function getLastId() { + return $this->driver->getLastId(); + } + + private function Cache($sql) { + if(class_exists('Caches')) { + $cache = new Caches(); + + if (stripos($sql, "select") !== false) { + + if($cache->check($this->cachePrefix.md5($sql))) { + + return $cache->get($this->cachePrefix.md5($sql)); + + } else { + $cache->set($this->cachePrefix.md5($sql), $this->driver->query($sql)); + + return $this->driver->query($sql); + } + } else { + return $this->driver->query($sql); + } + } else { + return $this->driver->query($sql); + } + } +} +?> \ No newline at end of file diff --git a/system/ecompress/.htaccess b/system/ecompress/.htaccess new file mode 100644 index 0000000..c18ad26 --- /dev/null +++ b/system/ecompress/.htaccess @@ -0,0 +1 @@ +#php_value auto_prepend_file none \ No newline at end of file diff --git a/system/ecompress/JSMin.php b/system/ecompress/JSMin.php new file mode 100644 index 0000000..5771a14 --- /dev/null +++ b/system/ecompress/JSMin.php @@ -0,0 +1,456 @@ + + * $minifiedJs = JSMin::minify($js); + * + * + * This is a modified port of jsmin.c. Improvements: + * + * Does not choke on some regexp literals containing quote characters. E.g. /'/ + * + * Spaces are preserved after some add/sub operators, so they are not mistakenly + * converted to post-inc/dec. E.g. a + ++b -> a+ ++b + * + * Preserves multi-line comments that begin with /*! + * + * PHP 5 or higher is required. + * + * Permission is hereby granted to use this version of the library under the + * same terms as jsmin.c, which has the following license: + * + * -- + * Copyright (c) 2002 Douglas Crockford (www.crockford.com) + * + * 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 shall be used for Good, not Evil. + * + * 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. + * -- + * + * @package JSMin + * @author Ryan Grove (PHP port) + * @author Steve Clay (modifications + cleanup) + * @author Andrea Giammarchi (spaceBeforeRegExp) + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (PHP port) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://code.google.com/p/jsmin-php/ + */ + +class JSMin { + const ORD_LF = 10; + const ORD_SPACE = 32; + const ACTION_KEEP_A = 1; + const ACTION_DELETE_A = 2; + const ACTION_DELETE_A_B = 3; + + protected $a = "\n"; + protected $b = ''; + protected $input = ''; + protected $inputIndex = 0; + protected $inputLength = 0; + protected $lookAhead = null; + protected $output = ''; + protected $lastByteOut = ''; + protected $keptComment = ''; + + /** + * Minify Javascript. + * + * @param string $js Javascript to be minified + * + * @return string + */ + public static function minify($js) + { + $jsmin = new JSMin($js); + return $jsmin->min(); + } + + /** + * @param string $input + */ + public function __construct($input) + { + $this->input = $input; + } + + /** + * Perform minification, return result + * + * @return string + */ + public function min() + { + if ($this->output !== '') { // min already run + return $this->output; + } + + $mbIntEnc = null; + if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { + $mbIntEnc = mb_internal_encoding(); + mb_internal_encoding('8bit'); + } + + if (isset($this->input[0]) && $this->input[0] === "\xef") { + $this->input = substr($this->input, 3); + } + + $this->input = str_replace("\r\n", "\n", $this->input); + $this->inputLength = strlen($this->input); + + $this->action(self::ACTION_DELETE_A_B); + + while ($this->a !== null) { + // determine next command + $command = self::ACTION_KEEP_A; // default + if ($this->a === ' ') { + if (($this->lastByteOut === '+' || $this->lastByteOut === '-') + && ($this->b === $this->lastByteOut)) { + // Don't delete this space. If we do, the addition/subtraction + // could be parsed as a post-increment + } elseif (! $this->isAlphaNum($this->b)) { + $command = self::ACTION_DELETE_A; + } + } elseif ($this->a === "\n") { + if ($this->b === ' ') { + $command = self::ACTION_DELETE_A_B; + + // in case of mbstring.func_overload & 2, must check for null b, + // otherwise mb_strpos will give WARNING + } elseif ($this->b === null + || (false === strpos('{[(+-!~', $this->b) + && ! $this->isAlphaNum($this->b))) { + $command = self::ACTION_DELETE_A; + } + } elseif (! $this->isAlphaNum($this->a)) { + if ($this->b === ' ' + || ($this->b === "\n" + && (false === strpos('}])+-"\'', $this->a)))) { + $command = self::ACTION_DELETE_A_B; + } + } + $this->action($command); + } + $this->output = trim($this->output); + + if ($mbIntEnc !== null) { + mb_internal_encoding($mbIntEnc); + } + return $this->output; + } + + /** + * ACTION_KEEP_A = Output A. Copy B to A. Get the next B. + * ACTION_DELETE_A = Copy B to A. Get the next B. + * ACTION_DELETE_A_B = Get the next B. + * + * @param int $command + * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException + */ + protected function action($command) + { + // make sure we don't compress "a + ++b" to "a+++b", etc. + if ($command === self::ACTION_DELETE_A_B + && $this->b === ' ' + && ($this->a === '+' || $this->a === '-')) { + // Note: we're at an addition/substraction operator; the inputIndex + // will certainly be a valid index + if ($this->input[$this->inputIndex] === $this->a) { + // This is "+ +" or "- -". Don't delete the space. + $command = self::ACTION_KEEP_A; + } + } + + switch ($command) { + case self::ACTION_KEEP_A: // 1 + $this->output .= $this->a; + + if ($this->keptComment) { + $this->output = rtrim($this->output, "\n"); + $this->output .= $this->keptComment; + $this->keptComment = ''; + } + + $this->lastByteOut = $this->a; + + // fallthrough intentional + case self::ACTION_DELETE_A: // 2 + $this->a = $this->b; + if ($this->a === "'" || $this->a === '"') { // string literal + $str = $this->a; // in case needed for exception + for(;;) { + $this->output .= $this->a; + $this->lastByteOut = $this->a; + + $this->a = $this->get(); + if ($this->a === $this->b) { // end quote + break; + } + if ($this->isEOF($this->a)) { + $byte = $this->inputIndex - 1; + throw new JSMin_UnterminatedStringException( + "JSMin: Unterminated String at byte {$byte}: {$str}"); + } + $str .= $this->a; + if ($this->a === '\\') { + $this->output .= $this->a; + $this->lastByteOut = $this->a; + + $this->a = $this->get(); + $str .= $this->a; + } + } + } + + // fallthrough intentional + case self::ACTION_DELETE_A_B: // 3 + $this->b = $this->next(); + if ($this->b === '/' && $this->isRegexpLiteral()) { + $this->output .= $this->a . $this->b; + $pattern = '/'; // keep entire pattern in case we need to report it in the exception + for(;;) { + $this->a = $this->get(); + $pattern .= $this->a; + if ($this->a === '[') { + for(;;) { + $this->output .= $this->a; + $this->a = $this->get(); + $pattern .= $this->a; + if ($this->a === ']') { + break; + } + if ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + $pattern .= $this->a; + } + if ($this->isEOF($this->a)) { + throw new JSMin_UnterminatedRegExpException( + "JSMin: Unterminated set in RegExp at byte " + . $this->inputIndex .": {$pattern}"); + } + } + } + + if ($this->a === '/') { // end pattern + break; // while (true) + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + $pattern .= $this->a; + } elseif ($this->isEOF($this->a)) { + $byte = $this->inputIndex - 1; + throw new JSMin_UnterminatedRegExpException( + "JSMin: Unterminated RegExp at byte {$byte}: {$pattern}"); + } + $this->output .= $this->a; + $this->lastByteOut = $this->a; + } + $this->b = $this->next(); + } + // end case ACTION_DELETE_A_B + } + } + + /** + * @return bool + */ + protected function isRegexpLiteral() + { + if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) { + // we can't divide after these tokens + return true; + } + + // check if first non-ws token is "/" (see starts-regex.js) + $length = strlen($this->output); + if ($this->a === ' ' || $this->a === "\n") { + if ($length < 2) { // weird edge case + return true; + } + } + + // if the "/" follows a keyword, it must be a regexp, otherwise it's best to assume division + + $subject = $this->output . trim($this->a); + if (!preg_match('/(?:case|else|in|return|typeof)$/', $subject, $m)) { + // not a keyword + return false; + } + + // can't be sure it's a keyword yet (see not-regexp.js) + $charBeforeKeyword = substr($subject, 0 - strlen($m[0]) - 1, 1); + if ($this->isAlphaNum($charBeforeKeyword)) { + // this is really an identifier ending in a keyword, e.g. "xreturn" + return false; + } + + // it's a regexp. Remove unneeded whitespace after keyword + if ($this->a === ' ' || $this->a === "\n") { + $this->a = ''; + } + + return true; + } + + /** + * Return the next character from stdin. Watch out for lookahead. If the character is a control character, + * translate it to a space or linefeed. + * + * @return string + */ + protected function get() + { + $c = $this->lookAhead; + $this->lookAhead = null; + if ($c === null) { + // getc(stdin) + if ($this->inputIndex < $this->inputLength) { + $c = $this->input[$this->inputIndex]; + $this->inputIndex += 1; + } else { + $c = null; + } + } + if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) { + return $c; + } + if ($c === "\r") { + return "\n"; + } + return ' '; + } + + /** + * Does $a indicate end of input? + * + * @param string $a + * @return bool + */ + protected function isEOF($a) + { + return ord($a) <= self::ORD_LF; + } + + /** + * Get next char (without getting it). If is ctrl character, translate to a space or newline. + * + * @return string + */ + protected function peek() + { + $this->lookAhead = $this->get(); + return $this->lookAhead; + } + + /** + * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character. + * + * @param string $c + * + * @return bool + */ + protected function isAlphaNum($c) + { + return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126); + } + + /** + * Consume a single line comment from input (possibly retaining it) + */ + protected function consumeSingleLineComment() + { + $comment = ''; + while (true) { + $get = $this->get(); + $comment .= $get; + if (ord($get) <= self::ORD_LF) { // end of line reached + // if IE conditional comment + if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) { + $this->keptComment .= "/{$comment}"; + } + return; + } + } + } + + /** + * Consume a multiple line comment from input (possibly retaining it) + * + * @throws JSMin_UnterminatedCommentException + */ + protected function consumeMultipleLineComment() + { + $this->get(); + $comment = ''; + for(;;) { + $get = $this->get(); + if ($get === '*') { + if ($this->peek() === '/') { // end of comment reached + $this->get(); + if (0 === strpos($comment, '!')) { + // preserved by YUI Compressor + if (!$this->keptComment) { + // don't prepend a newline if two comments right after one another + $this->keptComment = "\n"; + } + $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n"; + } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) { + // IE conditional + $this->keptComment .= "/*{$comment}*/"; + } + return; + } + } elseif ($get === null) { + throw new JSMin_UnterminatedCommentException( + "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}"); + } + $comment .= $get; + } + } + + /** + * Get the next character, skipping over comments. Some comments may be preserved. + * + * @return string + */ + protected function next() + { + $get = $this->get(); + if ($get === '/') { + switch ($this->peek()) { + case '/': + $this->consumeSingleLineComment(); + $get = "\n"; + break; + case '*': + $this->consumeMultipleLineComment(); + $get = ' '; + break; + } + } + return $get; + } +} + +class JSMin_UnterminatedStringException extends Exception {} +class JSMin_UnterminatedCommentException extends Exception {} +class JSMin_UnterminatedRegExpException extends Exception {} \ No newline at end of file diff --git a/system/ecompress/JSMinPlus.php b/system/ecompress/JSMinPlus.php new file mode 100644 index 0000000..5a3c5bd --- /dev/null +++ b/system/ecompress/JSMinPlus.php @@ -0,0 +1,2086 @@ + + * + * Usage: $minified = JSMinPlus::minify($script [, $filename]) + * + * Versionlog (see also changelog.txt): + * 23-07-2011 - remove dynamic creation of OP_* and KEYWORD_* defines and declare them on top + * reduce memory footprint by minifying by block-scope + * some small byte-saving and performance improvements + * 12-05-2009 - fixed hook:colon precedence, fixed empty body in loop and if-constructs + * 18-04-2009 - fixed crashbug in PHP 5.2.9 and several other bugfixes + * 12-04-2009 - some small bugfixes and performance improvements + * 09-04-2009 - initial open sourced version 1.0 + * + * Latest version of this script: http://files.tweakers.net/jsminplus/jsminplus.zip + * + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): Tino Zijdel + * PHP port, modifications and minifier routine are (C) 2009-2011 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define('TOKEN_END', 1); +define('TOKEN_NUMBER', 2); +define('TOKEN_IDENTIFIER', 3); +define('TOKEN_STRING', 4); +define('TOKEN_REGEXP', 5); +define('TOKEN_NEWLINE', 6); +define('TOKEN_CONDCOMMENT_START', 7); +define('TOKEN_CONDCOMMENT_END', 8); + +define('JS_SCRIPT', 100); +define('JS_BLOCK', 101); +define('JS_LABEL', 102); +define('JS_FOR_IN', 103); +define('JS_CALL', 104); +define('JS_NEW_WITH_ARGS', 105); +define('JS_INDEX', 106); +define('JS_ARRAY_INIT', 107); +define('JS_OBJECT_INIT', 108); +define('JS_PROPERTY_INIT', 109); +define('JS_GETTER', 110); +define('JS_SETTER', 111); +define('JS_GROUP', 112); +define('JS_LIST', 113); + +define('JS_MINIFIED', 999); + +define('DECLARED_FORM', 0); +define('EXPRESSED_FORM', 1); +define('STATEMENT_FORM', 2); + +/* Operators */ +define('OP_SEMICOLON', ';'); +define('OP_COMMA', ','); +define('OP_HOOK', '?'); +define('OP_COLON', ':'); +define('OP_OR', '||'); +define('OP_AND', '&&'); +define('OP_BITWISE_OR', '|'); +define('OP_BITWISE_XOR', '^'); +define('OP_BITWISE_AND', '&'); +define('OP_STRICT_EQ', '==='); +define('OP_EQ', '=='); +define('OP_ASSIGN', '='); +define('OP_STRICT_NE', '!=='); +define('OP_NE', '!='); +define('OP_LSH', '<<'); +define('OP_LE', '<='); +define('OP_LT', '<'); +define('OP_URSH', '>>>'); +define('OP_RSH', '>>'); +define('OP_GE', '>='); +define('OP_GT', '>'); +define('OP_INCREMENT', '++'); +define('OP_DECREMENT', '--'); +define('OP_PLUS', '+'); +define('OP_MINUS', '-'); +define('OP_MUL', '*'); +define('OP_DIV', '/'); +define('OP_MOD', '%'); +define('OP_NOT', '!'); +define('OP_BITWISE_NOT', '~'); +define('OP_DOT', '.'); +define('OP_LEFT_BRACKET', '['); +define('OP_RIGHT_BRACKET', ']'); +define('OP_LEFT_CURLY', '{'); +define('OP_RIGHT_CURLY', '}'); +define('OP_LEFT_PAREN', '('); +define('OP_RIGHT_PAREN', ')'); +define('OP_CONDCOMMENT_END', '@*/'); + +define('OP_UNARY_PLUS', 'U+'); +define('OP_UNARY_MINUS', 'U-'); + +/* Keywords */ +define('KEYWORD_BREAK', 'break'); +define('KEYWORD_CASE', 'case'); +define('KEYWORD_CATCH', 'catch'); +define('KEYWORD_CONST', 'const'); +define('KEYWORD_CONTINUE', 'continue'); +define('KEYWORD_DEBUGGER', 'debugger'); +define('KEYWORD_DEFAULT', 'default'); +define('KEYWORD_DELETE', 'delete'); +define('KEYWORD_DO', 'do'); +define('KEYWORD_ELSE', 'else'); +define('KEYWORD_ENUM', 'enum'); +define('KEYWORD_FALSE', 'false'); +define('KEYWORD_FINALLY', 'finally'); +define('KEYWORD_FOR', 'for'); +define('KEYWORD_FUNCTION', 'function'); +define('KEYWORD_IF', 'if'); +define('KEYWORD_IN', 'in'); +define('KEYWORD_INSTANCEOF', 'instanceof'); +define('KEYWORD_NEW', 'new'); +define('KEYWORD_NULL', 'null'); +define('KEYWORD_RETURN', 'return'); +define('KEYWORD_SWITCH', 'switch'); +define('KEYWORD_THIS', 'this'); +define('KEYWORD_THROW', 'throw'); +define('KEYWORD_TRUE', 'true'); +define('KEYWORD_TRY', 'try'); +define('KEYWORD_TYPEOF', 'typeof'); +define('KEYWORD_VAR', 'var'); +define('KEYWORD_VOID', 'void'); +define('KEYWORD_WHILE', 'while'); +define('KEYWORD_WITH', 'with'); + + +class JSMinPlus +{ + private $parser; + private $reserved = array( + 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', + 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', + 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', + 'void', 'while', 'with', + // Words reserved for future use + 'abstract', 'boolean', 'byte', 'char', 'class', 'const', 'debugger', + 'double', 'enum', 'export', 'extends', 'final', 'float', 'goto', + 'implements', 'import', 'int', 'interface', 'long', 'native', + 'package', 'private', 'protected', 'public', 'short', 'static', + 'super', 'synchronized', 'throws', 'transient', 'volatile', + // These are not reserved, but should be taken into account + // in isValidIdentifier (See jslint source code) + 'arguments', 'eval', 'true', 'false', 'Infinity', 'NaN', 'null', 'undefined' + ); + + private function __construct() + { + $this->parser = new JSParser($this); + } + + public static function minify($js, $filename='') + { + static $instance; + + // this is a singleton + if(!$instance) + $instance = new JSMinPlus(); + + return $instance->min($js, $filename); + } + + private function min($js, $filename) + { + try + { + $n = $this->parser->parse($js, $filename, 1); + return $this->parseTree($n); + } + catch(Exception $e) + { + echo $e->getMessage() . "\n"; + } + + return false; + } + + public function parseTree($n, $noBlockGrouping = false) + { + $s = ''; + + switch ($n->type) + { + case JS_MINIFIED: + $s = $n->value; + break; + + case JS_SCRIPT: + // we do nothing yet with funDecls or varDecls + $noBlockGrouping = true; + // FALL THROUGH + + case JS_BLOCK: + $childs = $n->treeNodes; + $lastType = 0; + for ($c = 0, $i = 0, $j = count($childs); $i < $j; $i++) + { + $type = $childs[$i]->type; + $t = $this->parseTree($childs[$i]); + if (strlen($t)) + { + if ($c) + { + $s = rtrim($s, ';'); + + if ($type == KEYWORD_FUNCTION && $childs[$i]->functionForm == DECLARED_FORM) + { + // put declared functions on a new line + $s .= "\n"; + } + elseif ($type == KEYWORD_VAR && $type == $lastType) + { + // mutiple var-statements can go into one + $t = ',' . substr($t, 4); + } + else + { + // add terminator + $s .= ';'; + } + } + + $s .= $t; + + $c++; + $lastType = $type; + } + } + + if ($c > 1 && !$noBlockGrouping) + { + $s = '{' . $s . '}'; + } + break; + + case KEYWORD_FUNCTION: + $s .= 'function' . ($n->name ? ' ' . $n->name : '') . '('; + $params = $n->params; + for ($i = 0, $j = count($params); $i < $j; $i++) + $s .= ($i ? ',' : '') . $params[$i]; + $s .= '){' . $this->parseTree($n->body, true) . '}'; + break; + + case KEYWORD_IF: + $s = 'if(' . $this->parseTree($n->condition) . ')'; + $thenPart = $this->parseTree($n->thenPart); + $elsePart = $n->elsePart ? $this->parseTree($n->elsePart) : null; + + // empty if-statement + if ($thenPart == '') + $thenPart = ';'; + + if ($elsePart) + { + // be carefull and always make a block out of the thenPart; could be more optimized but is a lot of trouble + if ($thenPart != ';' && $thenPart[0] != '{') + $thenPart = '{' . $thenPart . '}'; + + $s .= $thenPart . 'else'; + + // we could check for more, but that hardly ever applies so go for performance + if ($elsePart[0] != '{') + $s .= ' '; + + $s .= $elsePart; + } + else + { + $s .= $thenPart; + } + break; + + case KEYWORD_SWITCH: + $s = 'switch(' . $this->parseTree($n->discriminant) . '){'; + $cases = $n->cases; + for ($i = 0, $j = count($cases); $i < $j; $i++) + { + $case = $cases[$i]; + if ($case->type == KEYWORD_CASE) + $s .= 'case' . ($case->caseLabel->type != TOKEN_STRING ? ' ' : '') . $this->parseTree($case->caseLabel) . ':'; + else + $s .= 'default:'; + + $statement = $this->parseTree($case->statements, true); + if ($statement) + { + $s .= $statement; + // no terminator for last statement + if ($i + 1 < $j) + $s .= ';'; + } + } + $s .= '}'; + break; + + case KEYWORD_FOR: + $s = 'for(' . ($n->setup ? $this->parseTree($n->setup) : '') + . ';' . ($n->condition ? $this->parseTree($n->condition) : '') + . ';' . ($n->update ? $this->parseTree($n->update) : '') . ')'; + + $body = $this->parseTree($n->body); + if ($body == '') + $body = ';'; + + $s .= $body; + break; + + case KEYWORD_WHILE: + $s = 'while(' . $this->parseTree($n->condition) . ')'; + + $body = $this->parseTree($n->body); + if ($body == '') + $body = ';'; + + $s .= $body; + break; + + case JS_FOR_IN: + $s = 'for(' . ($n->varDecl ? $this->parseTree($n->varDecl) : $this->parseTree($n->iterator)) . ' in ' . $this->parseTree($n->object) . ')'; + + $body = $this->parseTree($n->body); + if ($body == '') + $body = ';'; + + $s .= $body; + break; + + case KEYWORD_DO: + $s = 'do{' . $this->parseTree($n->body, true) . '}while(' . $this->parseTree($n->condition) . ')'; + break; + + case KEYWORD_BREAK: + case KEYWORD_CONTINUE: + $s = $n->value . ($n->label ? ' ' . $n->label : ''); + break; + + case KEYWORD_TRY: + $s = 'try{' . $this->parseTree($n->tryBlock, true) . '}'; + $catchClauses = $n->catchClauses; + for ($i = 0, $j = count($catchClauses); $i < $j; $i++) + { + $t = $catchClauses[$i]; + $s .= 'catch(' . $t->varName . ($t->guard ? ' if ' . $this->parseTree($t->guard) : '') . '){' . $this->parseTree($t->block, true) . '}'; + } + if ($n->finallyBlock) + $s .= 'finally{' . $this->parseTree($n->finallyBlock, true) . '}'; + break; + + case KEYWORD_THROW: + case KEYWORD_RETURN: + $s = $n->type; + if ($n->value) + { + $t = $this->parseTree($n->value); + if (strlen($t)) + { + if ($this->isWordChar($t[0]) || $t[0] == '\\') + $s .= ' '; + + $s .= $t; + } + } + break; + + case KEYWORD_WITH: + $s = 'with(' . $this->parseTree($n->object) . ')' . $this->parseTree($n->body); + break; + + case KEYWORD_VAR: + case KEYWORD_CONST: + $s = $n->value . ' '; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $childs[$i]; + $s .= ($i ? ',' : '') . $t->name; + $u = $t->initializer; + if ($u) + $s .= '=' . $this->parseTree($u); + } + break; + + case KEYWORD_IN: + case KEYWORD_INSTANCEOF: + $left = $this->parseTree($n->treeNodes[0]); + $right = $this->parseTree($n->treeNodes[1]); + + $s = $left; + + if ($this->isWordChar(substr($left, -1))) + $s .= ' '; + + $s .= $n->type; + + if ($this->isWordChar($right[0]) || $right[0] == '\\') + $s .= ' '; + + $s .= $right; + break; + + case KEYWORD_DELETE: + case KEYWORD_TYPEOF: + $right = $this->parseTree($n->treeNodes[0]); + + $s = $n->type; + + if ($this->isWordChar($right[0]) || $right[0] == '\\') + $s .= ' '; + + $s .= $right; + break; + + case KEYWORD_VOID: + $s = 'void(' . $this->parseTree($n->treeNodes[0]) . ')'; + break; + + case KEYWORD_DEBUGGER: + throw new Exception('NOT IMPLEMENTED: DEBUGGER'); + break; + + case TOKEN_CONDCOMMENT_START: + case TOKEN_CONDCOMMENT_END: + $s = $n->value . ($n->type == TOKEN_CONDCOMMENT_START ? ' ' : ''); + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= $this->parseTree($childs[$i]); + break; + + case OP_SEMICOLON: + if ($expression = $n->expression) + $s = $this->parseTree($expression); + break; + + case JS_LABEL: + $s = $n->label . ':' . $this->parseTree($n->statement); + break; + + case OP_COMMA: + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + break; + + case OP_ASSIGN: + $s = $this->parseTree($n->treeNodes[0]) . $n->value . $this->parseTree($n->treeNodes[1]); + break; + + case OP_HOOK: + $s = $this->parseTree($n->treeNodes[0]) . '?' . $this->parseTree($n->treeNodes[1]) . ':' . $this->parseTree($n->treeNodes[2]); + break; + + case OP_OR: case OP_AND: + case OP_BITWISE_OR: case OP_BITWISE_XOR: case OP_BITWISE_AND: + case OP_EQ: case OP_NE: case OP_STRICT_EQ: case OP_STRICT_NE: + case OP_LT: case OP_LE: case OP_GE: case OP_GT: + case OP_LSH: case OP_RSH: case OP_URSH: + case OP_MUL: case OP_DIV: case OP_MOD: + $s = $this->parseTree($n->treeNodes[0]) . $n->type . $this->parseTree($n->treeNodes[1]); + break; + + case OP_PLUS: + case OP_MINUS: + $left = $this->parseTree($n->treeNodes[0]); + $right = $this->parseTree($n->treeNodes[1]); + + switch ($n->treeNodes[1]->type) + { + case OP_PLUS: + case OP_MINUS: + case OP_INCREMENT: + case OP_DECREMENT: + case OP_UNARY_PLUS: + case OP_UNARY_MINUS: + $s = $left . $n->type . ' ' . $right; + break; + + case TOKEN_STRING: + //combine concatted strings with same quotestyle + if ($n->type == OP_PLUS && substr($left, -1) == $right[0]) + { + $s = substr($left, 0, -1) . substr($right, 1); + break; + } + // FALL THROUGH + + default: + $s = $left . $n->type . $right; + } + break; + + case OP_NOT: + case OP_BITWISE_NOT: + case OP_UNARY_PLUS: + case OP_UNARY_MINUS: + $s = $n->value . $this->parseTree($n->treeNodes[0]); + break; + + case OP_INCREMENT: + case OP_DECREMENT: + if ($n->postfix) + $s = $this->parseTree($n->treeNodes[0]) . $n->value; + else + $s = $n->value . $this->parseTree($n->treeNodes[0]); + break; + + case OP_DOT: + $s = $this->parseTree($n->treeNodes[0]) . '.' . $this->parseTree($n->treeNodes[1]); + break; + + case JS_INDEX: + $s = $this->parseTree($n->treeNodes[0]); + // See if we can replace named index with a dot saving 3 bytes + if ( $n->treeNodes[0]->type == TOKEN_IDENTIFIER && + $n->treeNodes[1]->type == TOKEN_STRING && + $this->isValidIdentifier(substr($n->treeNodes[1]->value, 1, -1)) + ) + $s .= '.' . substr($n->treeNodes[1]->value, 1, -1); + else + $s .= '[' . $this->parseTree($n->treeNodes[1]) . ']'; + break; + + case JS_LIST: + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + break; + + case JS_CALL: + $s = $this->parseTree($n->treeNodes[0]) . '(' . $this->parseTree($n->treeNodes[1]) . ')'; + break; + + case KEYWORD_NEW: + case JS_NEW_WITH_ARGS: + $s = 'new ' . $this->parseTree($n->treeNodes[0]) . '(' . ($n->type == JS_NEW_WITH_ARGS ? $this->parseTree($n->treeNodes[1]) : '') . ')'; + break; + + case JS_ARRAY_INIT: + $s = '['; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + } + $s .= ']'; + break; + + case JS_OBJECT_INIT: + $s = '{'; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $childs[$i]; + if ($i) + $s .= ','; + if ($t->type == JS_PROPERTY_INIT) + { + // Ditch the quotes when the index is a valid identifier + if ( $t->treeNodes[0]->type == TOKEN_STRING && + $this->isValidIdentifier(substr($t->treeNodes[0]->value, 1, -1)) + ) + $s .= substr($t->treeNodes[0]->value, 1, -1); + else + $s .= $t->treeNodes[0]->value; + + $s .= ':' . $this->parseTree($t->treeNodes[1]); + } + else + { + $s .= $t->type == JS_GETTER ? 'get' : 'set'; + $s .= ' ' . $t->name . '('; + $params = $t->params; + for ($i = 0, $j = count($params); $i < $j; $i++) + $s .= ($i ? ',' : '') . $params[$i]; + $s .= '){' . $this->parseTree($t->body, true) . '}'; + } + } + $s .= '}'; + break; + + case TOKEN_NUMBER: + $s = $n->value; + if (preg_match('/^([1-9]+)(0{3,})$/', $s, $m)) + $s = $m[1] . 'e' . strlen($m[2]); + break; + + case KEYWORD_NULL: case KEYWORD_THIS: case KEYWORD_TRUE: case KEYWORD_FALSE: + case TOKEN_IDENTIFIER: case TOKEN_STRING: case TOKEN_REGEXP: + $s = $n->value; + break; + + case JS_GROUP: + if (in_array( + $n->treeNodes[0]->type, + array( + JS_ARRAY_INIT, JS_OBJECT_INIT, JS_GROUP, + TOKEN_NUMBER, TOKEN_STRING, TOKEN_REGEXP, TOKEN_IDENTIFIER, + KEYWORD_NULL, KEYWORD_THIS, KEYWORD_TRUE, KEYWORD_FALSE + ) + )) + { + $s = $this->parseTree($n->treeNodes[0]); + } + else + { + $s = '(' . $this->parseTree($n->treeNodes[0]) . ')'; + } + break; + + default: + throw new Exception('UNKNOWN TOKEN TYPE: ' . $n->type); + } + + return $s; + } + + private function isValidIdentifier($string) + { + return preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $string) && !in_array($string, $this->reserved); + } + + private function isWordChar($char) + { + return $char == '_' || $char == '$' || ctype_alnum($char); + } +} + +class JSParser +{ + private $t; + private $minifier; + + private $opPrecedence = array( + ';' => 0, + ',' => 1, + '=' => 2, '?' => 2, ':' => 2, + // The above all have to have the same precedence, see bug 330975 + '||' => 4, + '&&' => 5, + '|' => 6, + '^' => 7, + '&' => 8, + '==' => 9, '!=' => 9, '===' => 9, '!==' => 9, + '<' => 10, '<=' => 10, '>=' => 10, '>' => 10, 'in' => 10, 'instanceof' => 10, + '<<' => 11, '>>' => 11, '>>>' => 11, + '+' => 12, '-' => 12, + '*' => 13, '/' => 13, '%' => 13, + 'delete' => 14, 'void' => 14, 'typeof' => 14, + '!' => 14, '~' => 14, 'U+' => 14, 'U-' => 14, + '++' => 15, '--' => 15, + 'new' => 16, + '.' => 17, + JS_NEW_WITH_ARGS => 0, JS_INDEX => 0, JS_CALL => 0, + JS_ARRAY_INIT => 0, JS_OBJECT_INIT => 0, JS_GROUP => 0 + ); + + private $opArity = array( + ',' => -2, + '=' => 2, + '?' => 3, + '||' => 2, + '&&' => 2, + '|' => 2, + '^' => 2, + '&' => 2, + '==' => 2, '!=' => 2, '===' => 2, '!==' => 2, + '<' => 2, '<=' => 2, '>=' => 2, '>' => 2, 'in' => 2, 'instanceof' => 2, + '<<' => 2, '>>' => 2, '>>>' => 2, + '+' => 2, '-' => 2, + '*' => 2, '/' => 2, '%' => 2, + 'delete' => 1, 'void' => 1, 'typeof' => 1, + '!' => 1, '~' => 1, 'U+' => 1, 'U-' => 1, + '++' => 1, '--' => 1, + 'new' => 1, + '.' => 2, + JS_NEW_WITH_ARGS => 2, JS_INDEX => 2, JS_CALL => 2, + JS_ARRAY_INIT => 1, JS_OBJECT_INIT => 1, JS_GROUP => 1, + TOKEN_CONDCOMMENT_START => 1, TOKEN_CONDCOMMENT_END => 1 + ); + + public function __construct($minifier=null) + { + $this->minifier = $minifier; + $this->t = new JSTokenizer(); + } + + public function parse($s, $f, $l) + { + // initialize tokenizer + $this->t->init($s, $f, $l); + + $x = new JSCompilerContext(false); + $n = $this->Script($x); + if (!$this->t->isDone()) + throw $this->t->newSyntaxError('Syntax error'); + + return $n; + } + + private function Script($x) + { + $n = $this->Statements($x); + $n->type = JS_SCRIPT; + $n->funDecls = $x->funDecls; + $n->varDecls = $x->varDecls; + + // minify by scope + if ($this->minifier) + { + $n->value = $this->minifier->parseTree($n); + + // clear tree from node to save memory + $n->treeNodes = null; + $n->funDecls = null; + $n->varDecls = null; + + $n->type = JS_MINIFIED; + } + + return $n; + } + + private function Statements($x) + { + $n = new JSNode($this->t, JS_BLOCK); + array_push($x->stmtStack, $n); + + while (!$this->t->isDone() && $this->t->peek() != OP_RIGHT_CURLY) + $n->addNode($this->Statement($x)); + + array_pop($x->stmtStack); + + return $n; + } + + private function Block($x) + { + $this->t->mustMatch(OP_LEFT_CURLY); + $n = $this->Statements($x); + $this->t->mustMatch(OP_RIGHT_CURLY); + + return $n; + } + + private function Statement($x) + { + $tt = $this->t->get(); + $n2 = null; + + // Cases for statements ending in a right curly return early, avoiding the + // common semicolon insertion magic after this switch. + switch ($tt) + { + case KEYWORD_FUNCTION: + return $this->FunctionDefinition( + $x, + true, + count($x->stmtStack) > 1 ? STATEMENT_FORM : DECLARED_FORM + ); + break; + + case OP_LEFT_CURLY: + $n = $this->Statements($x); + $this->t->mustMatch(OP_RIGHT_CURLY); + return $n; + + case KEYWORD_IF: + $n = new JSNode($this->t); + $n->condition = $this->ParenExpression($x); + array_push($x->stmtStack, $n); + $n->thenPart = $this->Statement($x); + $n->elsePart = $this->t->match(KEYWORD_ELSE) ? $this->Statement($x) : null; + array_pop($x->stmtStack); + return $n; + + case KEYWORD_SWITCH: + $n = new JSNode($this->t); + $this->t->mustMatch(OP_LEFT_PAREN); + $n->discriminant = $this->Expression($x); + $this->t->mustMatch(OP_RIGHT_PAREN); + $n->cases = array(); + $n->defaultIndex = -1; + + array_push($x->stmtStack, $n); + + $this->t->mustMatch(OP_LEFT_CURLY); + + while (($tt = $this->t->get()) != OP_RIGHT_CURLY) + { + switch ($tt) + { + case KEYWORD_DEFAULT: + if ($n->defaultIndex >= 0) + throw $this->t->newSyntaxError('More than one switch default'); + // FALL THROUGH + case KEYWORD_CASE: + $n2 = new JSNode($this->t); + if ($tt == KEYWORD_DEFAULT) + $n->defaultIndex = count($n->cases); + else + $n2->caseLabel = $this->Expression($x, OP_COLON); + break; + default: + throw $this->t->newSyntaxError('Invalid switch case'); + } + + $this->t->mustMatch(OP_COLON); + $n2->statements = new JSNode($this->t, JS_BLOCK); + while (($tt = $this->t->peek()) != KEYWORD_CASE && $tt != KEYWORD_DEFAULT && $tt != OP_RIGHT_CURLY) + $n2->statements->addNode($this->Statement($x)); + + array_push($n->cases, $n2); + } + + array_pop($x->stmtStack); + return $n; + + case KEYWORD_FOR: + $n = new JSNode($this->t); + $n->isLoop = true; + $this->t->mustMatch(OP_LEFT_PAREN); + + if (($tt = $this->t->peek()) != OP_SEMICOLON) + { + $x->inForLoopInit = true; + if ($tt == KEYWORD_VAR || $tt == KEYWORD_CONST) + { + $this->t->get(); + $n2 = $this->Variables($x); + } + else + { + $n2 = $this->Expression($x); + } + $x->inForLoopInit = false; + } + + if ($n2 && $this->t->match(KEYWORD_IN)) + { + $n->type = JS_FOR_IN; + if ($n2->type == KEYWORD_VAR) + { + if (count($n2->treeNodes) != 1) + { + throw $this->t->SyntaxError( + 'Invalid for..in left-hand side', + $this->t->filename, + $n2->lineno + ); + } + + // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name. + $n->iterator = $n2->treeNodes[0]; + $n->varDecl = $n2; + } + else + { + $n->iterator = $n2; + $n->varDecl = null; + } + + $n->object = $this->Expression($x); + } + else + { + $n->setup = $n2 ? $n2 : null; + $this->t->mustMatch(OP_SEMICOLON); + $n->condition = $this->t->peek() == OP_SEMICOLON ? null : $this->Expression($x); + $this->t->mustMatch(OP_SEMICOLON); + $n->update = $this->t->peek() == OP_RIGHT_PAREN ? null : $this->Expression($x); + } + + $this->t->mustMatch(OP_RIGHT_PAREN); + $n->body = $this->nest($x, $n); + return $n; + + case KEYWORD_WHILE: + $n = new JSNode($this->t); + $n->isLoop = true; + $n->condition = $this->ParenExpression($x); + $n->body = $this->nest($x, $n); + return $n; + + case KEYWORD_DO: + $n = new JSNode($this->t); + $n->isLoop = true; + $n->body = $this->nest($x, $n, KEYWORD_WHILE); + $n->condition = $this->ParenExpression($x); + if (!$x->ecmaStrictMode) + { + // '; + } + +} else { + // Enable caching + header('Cache-Control: public'); + + // Enable GZip encoding. + ob_start("ob_gzhandler"); + + // Expire in one day + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 86400) . ' GMT'); + //header('Accept-Encoding: deflate'); + //header('Content-Encoding: deflate'); + + // Set the correct MIME type, because Apache won't set it for us + + if (in_array($extension, array('css'))) { + header("Content-Type: text/".$extension); + } else { + header("Content-Type: text/javascript"); + } + +} +// Write everything out +echo $buffer; + + +?> \ No newline at end of file diff --git a/system/ecompress/includes.php b/system/ecompress/includes.php new file mode 100644 index 0000000..a10261b --- /dev/null +++ b/system/ecompress/includes.php @@ -0,0 +1,21 @@ + strtotime("-$cache_days days") and $utilize_cache == true and $reset_cache == false) { + header('Content-Length: '.filesize($cache_file)); + readfile($cache_file); + +} else { + + if($utilize_cache == true) { + if (!is_dir("$document_root/$cache_path")) { // no + if (!mkdir("$document_root/$cache_path", 0755, true)) { // so make it + if (!is_dir("$document_root/$cache_path")) { // check again to protect against race conditions + // uh-oh, failed to make that directory + echo("Failed to create cache directory at: $document_root/$cache_path"); + } + } + } + + if (!is_dir("$document_root/$cache_path/$extension")) { // no + if (!mkdir("$document_root/$cache_path/$extension", 0755, true)) { // so make it + if (!is_dir("$document_root/$cache_path/$extension")) { // check again to protect against race conditions + // uh-oh, failed to make that directory + echo("Failed to create cache directory at: $document_root/$cache_path"); + } + } + } + } + + if($utilize_cache == true) { + + $parts = explode('/', $cache_file); + $file = array_pop($parts); + $dir = ''; + foreach($parts as $part) { + if(!is_dir($dir .= "/$part")) mkdir($dir, 0755, true); + } + //file_put_contents($cache_file, $buffer); + } + + if (in_array($extension, array('png'))) { + + if($utilize_cache == true) { + if($Imagick_compress == true) { + $image = new Imagick($source_file); + $image->setImageFormat("png"); + $image->setImageCompression(Imagick::COMPRESSION_UNDEFINED); + $image->setImageCompressionQuality($PNG_compress_imagick); + $image->stripImage(); + $image->writeImage($cache_file); + header('Content-Length: '.filesize($cache_file)); + echo $image; + } else { + $imagemPNG = imagecreatefrompng($source_file) or die("Não foi possível inicializar uma nova imagem"); + imagealphablending($imagemPNG, false); + imagesavealpha($imagemPNG, true); + imagepng($imagemPNG, $cache_file, $PNG_compress_GD); + header('Content-Length: '.filesize($cache_file)); + readfile($cache_file); + } + } else { + if($Imagick_compress == true) { + $image = new Imagick($source_file); + //$image->setImageFormat("png"); + $image->setImageCompression(Imagick::COMPRESSION_UNDEFINED); + $image->setImageCompressionQuality($PNG_compress_imagick); + $image->stripImage(); + echo $image; + } else { + $imagemPNG = imagecreatefrompng($source_file) or die("Não foi possível inicializar uma nova imagem"); + imagealphablending($imagemPNG, false); + imagesavealpha($imagemPNG, true); + imagepng($imagemPNG, NULL, $PNG_compress_GD); + } + } + + // Liberar memória + imagedestroy($imagemPNG); + } + + if (in_array($extension, array('jpg', 'jpeg'))) { + if($utilize_cache == true) { + if($Imagick_compress == true) { + $image = new Imagick($source_file); + //$image->setImageFormat("jpeg"); + $image->setImageCompression(Imagick::COMPRESSION_LOSSLESSJPEG); + $image->setImageCompressionQuality($JPG_quality); + $image->stripImage(); + $image->writeImage($cache_file); + header('Content-Length: '.filesize($cache_file)); + echo $image; + } else { + $imagemJPG = imagecreatefromjpeg($source_file) or die("Não foi possível inicializar uma nova imagem"); + imagejpeg($imagemJPG, $cache_file, $JPG_quality); + header('Content-Length: '.filesize($cache_file)); + readfile($cache_file); + } + } else { + if($Imagick_compress == true) { + $image = new Imagick($source_file); + //$image->setImageFormat("jpeg"); + $image->setImageCompression(Imagick::COMPRESSION_LOSSLESSJPEG); + $image->setImageCompressionQuality($JPG_quality); + $image->stripImage(); + echo $image; + } else { + $imagemJPG = imagecreatefromjpeg($source_file) or die("Não foi possível inicializar uma nova imagem"); + imagejpeg($imagemJPG, NULL, $JPG_quality); + } + } + + // Liberar memória + imagedestroy($imagemJPG); + } + +} + + \ No newline at end of file diff --git a/system/ecompress/phpminihtml.php b/system/ecompress/phpminihtml.php new file mode 100644 index 0000000..060380e --- /dev/null +++ b/system/ecompress/phpminihtml.php @@ -0,0 +1,278 @@ +process(); + } + + + /** + * Create a minifier object + * + * @param string $html + * + * @param array $options + * + * 'cssMinifier' : (optional) callback function to process content of STYLE + * elements. + * + * 'jsMinifier' : (optional) callback function to process content of SCRIPT + * elements. Note: the type attribute is ignored. + * + * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block + * + * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If + * unset, minify will sniff for an XHTML doctype. + */ + public function __construct($html, $options = array()) + { + $this->_html = str_replace("\r\n", "\n", trim($html)); + if (isset($options['xhtml'])) { + $this->_isXhtml = (bool)$options['xhtml']; + } + if (isset($options['cssMinifier'])) { + $this->_cssMinifier = $options['cssMinifier']; + } + if (isset($options['jsMinifier'])) { + $this->_jsMinifier = $options['jsMinifier']; + } + if (isset($options['jsCleanComments'])) { + $this->_jsCleanComments = (bool)$options['jsCleanComments']; + } + } + + + /** + * Minify the markeup given in the constructor + * + * @return string + */ + public function process() + { + if ($this->_isXhtml === null) { + $this->_isXhtml = (false !== strpos($this->_html, '_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); + $this->_placeholders = array(); + + // replace SCRIPTs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/(\\s*)]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' + ,array($this, '_removeScriptCB') + ,$this->_html); + + // replace STYLEs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/\\s*]*>)([\\s\\S]*?)<\\/style>\\s*/i' + ,array($this, '_removeStyleCB') + ,$this->_html); + + // remove HTML comments (not containing IE conditional comments). + $this->_html = preg_replace_callback( + '//' + ,array($this, '_commentCB') + ,$this->_html); + + // replace PREs with placeholders + $this->_html = preg_replace_callback('/\\s*]*?>[\\s\\S]*?<\\/pre>)\\s*/i' + ,array($this, '_removePreCB') + ,$this->_html); + + // replace TEXTAREAs with placeholders + $this->_html = preg_replace_callback( + '/\\s*]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' + ,array($this, '_removeTextareaCB') + ,$this->_html); + + // trim each line. + // @todo take into account attribute values that span multiple lines. + $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); + + // remove ws around block/undisplayed elements + $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' + .'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' + .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' + .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' + .'|ul)\\b[^>]*>)/i', '$1', $this->_html); + + // remove ws outside of all elements + $this->_html = preg_replace( + '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?$1$2$3<' + ,$this->_html); + + // use newlines before 1st attribute in open tags (to limit line lengths) + $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); + + // fill placeholders + $this->_html = str_replace( + array_keys($this->_placeholders) + ,array_values($this->_placeholders) + ,$this->_html + ); + // issue 229: multi-pass to catch scripts that didn't get replaced in textareas + $this->_html = str_replace( + array_keys($this->_placeholders) + ,array_values($this->_placeholders) + ,$this->_html + ); + return $this->_html; + } + + protected function _commentCB($m) + { + return (0 === strpos($m[1], '[') || false !== strpos($m[1], '_replacementHash . count($this->_placeholders) . '%'; + $this->_placeholders[$placeholder] = $content; + return $placeholder; + } + + protected $_isXhtml = null; + protected $_replacementHash = null; + protected $_placeholders = array(); + protected $_cssMinifier = null; + protected $_jsMinifier = null; + + protected function _removePreCB($m) + { + return $this->_reservePlace("_reservePlace("\\s*$)/', '', $css); + + // remove CDATA section markers + $css = $this->_removeCdata($css); + + // minify + $minifier = $this->_cssMinifier + ? $this->_cssMinifier + : 'trim'; + $css = call_user_func($minifier, $css); + + return $this->_reservePlace($this->_needsCdata($css) + ? "{$openStyle}/**/" + : "{$openStyle}{$css}" + ); + } + + protected function _removeScriptCB($m) + { + $openScript = "_jsCleanComments) { + $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); + } + + // remove CDATA section markers + $js = $this->_removeCdata($js); + + // minify + $minifier = $this->_jsMinifier + ? $this->_jsMinifier + : 'trim'; + $js = call_user_func($minifier, $js); + + return $this->_reservePlace($this->_needsCdata($js) + ? "{$ws1}{$openScript}/**/{$ws2}" + : "{$ws1}{$openScript}{$js}{$ws2}" + ); + } + + protected function _removeCdata($str) + { + return (false !== strpos($str, ''), '', $str) + : $str; + } + + protected function _needsCdata($str) + { + return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); + } +} + + +function sanitize_output($buffer3) { + + $search = array( + '/\>[^\S ]+/s', // strip whitespaces after tags, except space + '/[^\S ]+\', + '<', + '\\1' + ); + + //$buffer = preg_replace($search, $replace, $buffer); + //$buffer = preg_replace('//Uis', '', $buffer); + $needle = 'html'; + +/*$pos = strripos($buffer, $needle); +if ($pos === false) { +} else { + + $buffer = Minify_HTML::minify($buffer, array('xhtml')); +}*/ + $buffer3 = Minify_HTML::minify($buffer3, array('xhtml','jsMinifier','cssMinifier')); + + //$buffer = preg_replace('/(?:^\\s*\\s*$)/', '', $buffer); + + + return $buffer3; +} + +ob_start("sanitize_output"); +} +?> \ No newline at end of file diff --git a/system/engine/action.php b/system/engine/action.php new file mode 100644 index 0000000..6adece0 --- /dev/null +++ b/system/engine/action.php @@ -0,0 +1,126 @@ +file = DIR_APPLICATION . 'controller/' . str_replace('../', '', $path) . '.php'; + + $this->class = 'Controller' . preg_replace('/[^a-zA-Z0-9]/', '', $path); + + array_shift($parts); + + break; + } + } + + if ($args) { + $this->args = $args; + } + + $method = array_shift($parts); + + if ($method) { + $this->method = $method; + } else { + $this->method = 'index'; + } + } + + public function getFile() { + return $this->file; + } + + public function getClass() { + return $this->class; + } + + public function getMethod() { + return $this->method; + } + + public function getArgs() { + return $this->args; + } +} +final class ActionSystem { + protected $file; + protected $class; + protected $method; + protected $args = array(); + + public function __construct($route, $args = array()) { + $path = ''; + + $parts = explode('/', str_replace('../', '', (string)$route)); + + foreach ($parts as $part) { + $path .= $part; + + if (is_dir(DIR_SYSTEM . '' . $path)) { + $path .= '/'; + + array_shift($parts); + + continue; + } + + if (is_file(DIR_SYSTEM . '' . str_replace('../', '', $path) . '.php')) { + $this->file = DIR_SYSTEM . '' . str_replace('../', '', $path) . '.php'; + + $this->class = 'System' . preg_replace('/[^a-zA-Z0-9]/', '', $path); + + array_shift($parts); + + break; + } + } + + if ($args) { + $this->args = $args; + } + + $method = array_shift($parts); + + if ($method) { + $this->method = $method; + } else { + $this->method = 'index'; + } + } + + public function getFile() { + return $this->file; + } + + public function getClass() { + return $this->class; + } + + public function getMethod() { + return $this->method; + } + + public function getArgs() { + return $this->args; + } +} +?> \ No newline at end of file diff --git a/system/engine/autoload.php b/system/engine/autoload.php new file mode 100644 index 0000000..6cc8b77 --- /dev/null +++ b/system/engine/autoload.php @@ -0,0 +1,13 @@ +dirCache)) { + mkdir($this->dirCache, 0755, true); + } + $this->expire = (defined('CACHE_EXPIRE')) ? CACHE_EXPIRE : 3600; + $files = glob($this->dirCache . '*.cache'); + + if ($files) { + foreach ($files as $file) { + //$time = substr(strrchr($file, '.'), 1); + $time = substr(strrchr(strstr($file, '.', true), '/'), 1); + //var_dump(substr(strrchr(strstr($file, '.', true), '/'), 1)); + + if ($time < time() and $time !== '0') { + if (file_exists($file)) { + unlink($file); + clearstatcache(); + } + } + } + } + } + + public function verify($key) { + $files = glob($this->dirCache . '*.' . preg_replace('/[^A-Z0-9\._-]/i', '', $key) . '.cache'); + + if ($files) { + + return true; + + } else { + + return false; + } + } + + public function check($key) { + return $this->verify($key); + } + + public function get($key) { + $files = glob($this->dirCache . '*.' . preg_replace('/[^A-Z0-9\._-]/i', '', $key) . '.cache'); + + if ($files) { + $cache = file_get_contents($files[0]); + + return unserialize($cache); + } + } + + public function set($key, $value, $expire = true) { + $this->delete($key); + + $exp = ($expire == true) ? (time() + $this->expire) : 0; + + $file = $this->dirCache . $exp . '.' . preg_replace('/[^A-Z0-9\._-]/i', '', $key) . '.cache'; + + return file_put_contents($file, serialize($value)); + + } + + public function delete($key) { + $files = glob($this->dirCache . '*.' . preg_replace('/[^A-Z0-9\._-]/i', '', $key) . '.cache'); + + if ($files) { + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + clearstatcache(); + } + } + } + } +} \ No newline at end of file diff --git a/system/engine/classes.php b/system/engine/classes.php new file mode 100644 index 0000000..57e08ab --- /dev/null +++ b/system/engine/classes.php @@ -0,0 +1,65 @@ +format = $format; + + } + + public function classes($origin = NULL){ + $mClass = get_declared_classes(); + + $pegaKey = 0; + + foreach($mClass as $key => $value){ + if($value == 'startEngineExacTI'){ + $pegaKey = $key; + } + if($pegaKey != 0 and $key > $pegaKey){ + $pegaClass[] .= $value; + } + } + + if($this->format == "HTML" && $origin != 'intern'){ + $pegaClassD = $pegaClass; + $pegaClass = ''; + foreach($pegaClassD as $value) { + $pegaClass .= ''.$value.'
    '; + + } + } + + + return($pegaClass); + } + + public function functions(){ + $classes = $this->classes('intern'); + + $functions = array(); + + foreach($classes as $key => $value){ + $functions = array_merge($functions, array($value => get_class_methods($value))); + } + + if($this->format == "HTML"){ + $functions = ""; + foreach($classes as $value) { + $functions .= '
    '.$value.'
    '; + $subFunc = get_class_methods($value); + foreach($subFunc as $value) { + $functions .= ''.$value.'
    '; + } + } + } + + return $functions; + + } + + +} \ No newline at end of file diff --git a/system/engine/controller.php b/system/engine/controller.php new file mode 100644 index 0000000..548713b --- /dev/null +++ b/system/engine/controller.php @@ -0,0 +1,234 @@ +registry = $registry; + } + + public function __get($key) { + return $this->registry->get($key); + } + + public function __set($key, $value) { + $this->registry->set($key, $value); + } + + protected function forward($route, $args = array()) { + return new Action($route, $args); + } + + protected function redirect($url, $status = 302) { + header('Status: ' . $status); + header('Location: ' . str_replace('&', '&', $url)); + exit(); + } + + protected function getChild($child, $args = array()) { + $action = new Action($child, $args); + $file = $action->getFile(); + $class = $action->getClass(); + $method = $action->getMethod(); + + if (file_exists($file)) { + require_once($file); + + $controller = new $class($this->registry); + + $controller->$method($args); + + return $controller->output; + } else { + trigger_error('Error: Could not load controller ' . $child . '!'); + exit(); + } + } + + protected function render() { + + foreach ($this->children as $child) { + $this->data[basename($child)] = $this->getChild($child); + } + + if($this->template === NULL) { + $pegRout = explode("/", $this->request->get['route']); + + $this->templateTypes = array("tpl", "twig", "dwoo", "mustache", "smarty"); + + foreach($this->templateTypes as $extensionTemplate) { + if(isset($pegRout[2])){ + if (file_exists(DIR_TEMPLATE . 'default/'.$pegRout[0].'/'.$pegRout[1].'_'.$pegRout[2].'.'.$extensionTemplate) != false) { + $this->template = 'default/'.$pegRout[0].'/'.$pegRout[1].'_'.$pegRout[2].'.'.$extensionTemplate; + break; + } + } else { + if (file_exists(DIR_TEMPLATE . 'default/'.$pegRout[0].'/'.$pegRout[1].'.'.$extensionTemplate) != false) { + $this->template = 'default/'.$pegRout[0].'/'.$pegRout[1].'.'.$extensionTemplate; + break; + } + } + } + + } + + if (file_exists(DIR_TEMPLATE . $this->template)) { + + $templateType = substr(strrchr($this->template, '.'), 1); + + switch($templateType) { + case 'tpl': + extract($this->data); + + ob_start(); + require(DIR_TEMPLATE . $this->template); + + $this->output = ob_get_contents(); + + ob_end_clean(); + break; + + case 'twig': + require_once(DIR_SYSTEM."templateEngines/Twig/autoload.php"); + + $config = array( + 'autoescape' => false, + 'cache' => DIR_CACHE."twig/", + 'debug' => (defined('DEBUG')) ? DEBUG : false + ); + $loader = new Twig_Loader_Filesystem(DIR_TEMPLATE); + $twig = new Twig_Environment($loader, $config); + + if($config['debug']) { + $twig->addExtension(new Twig_Extension_Debug()); + } + + $twig->addExtension(new transExtension()); + + $twig->addFilter(new Twig_SimpleFilter('translate', function ($str) { + // do something different from the built-in date filter + return traduzir($str); + })); + + foreach ($this->twig as $key => $item) { + $twig->addFilter(new Twig_SimpleFilter($key, $item)); + } + + $template = $twig->load($this->template); + + $this->output = $template->render($this->data); + break; + + case 'dwoo': + require_once(DIR_SYSTEM."templateEngines/Dwoo/autoload.php"); + + $dwoo = new Dwoo\Core(); + + // Configure directories + $dwoo->setCompileDir(DIR_CACHE.'dwoo/compiled/dir/'); // Folder to store compiled templates + $dwoo->setCacheDir(DIR_CACHE.'dwoo/cache/');// Folder to store cached templates + $dwoo->setCacheTime(3600); //Cache Time + $dwoo->setTemplateDir(DIR_TEMPLATE); // Folder containing template files + + $dwoo->getLoader()->addDirectory(DIR_SYSTEM."templateEngines/Dwoo/extension/"); + + $this->output = $dwoo->get($this->template, $this->data); + + break; + + case 'mustache': + require_once(DIR_SYSTEM."templateEngines/Mustache/autoload.php"); + + Mustache_Autoloader::register(); + + $mustache = new Mustache_Engine(array( + //'template_class_prefix' => '__MyTemplates_', + 'cache' => DIR_CACHE.'mustache', + 'cache_file_mode' => 0666, // Please, configure your umask instead of doing this :) + //'cache_lambda_templates' => true, + 'loader' => new Mustache_Loader_FilesystemLoader(DIR_TEMPLATE), + //'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), + 'helpers' => array('translate' => function($text) { + if (class_exists('Translate')) { + $trans = new Translate(); + return ($trans->translation($text)); + } else { + return $text; + }// do something translatey here... + }), + /*'escape' => function($value) { + return $value; + },*/ + //'charset' => 'ISO-8859-1', + //'logger' => new Mustache_Logger_StreamLogger('php://stderr'), + //'strict_callables' => true, + //'pragmas' => [Mustache_Engine::PRAGMA_FILTERS], + )); + + $tpl = $mustache->loadTemplate($this->template); + $this->output = $tpl->render($this->data); + break; + + case 'smarty': + require_once(DIR_SYSTEM."templateEngines/smarty/autoload.php"); + + $smarty = new Smarty(); + + $smarty->setTemplateDir(DIR_TEMPLATE); + $smarty->setCompileDir(DIR_CACHE."Smarty/compile/"); + //$smarty->setConfigDir('/web/www.example.com/guestbook/configs/'); + $smarty->setCacheDir(DIR_CACHE."Smarty/cache/"); + + $smarty->registerPlugin("block","translate", "translate"); + + $smarty->assign($this->data); + + $smarty->caching = Smarty::CACHING_LIFETIME_CURRENT; + + //** un-comment the following line to show the debug console + $smarty->debugging = (defined('DEBUG')) ? DEBUG : false; + + $this->output = $smarty->display($this->template); + break; + + default: + extract($this->data); + + ob_start(); + require(DIR_TEMPLATE . $this->template); + + $this->output = ob_get_contents(); + + ob_end_clean(); + break; + + } + + return $this->output; + + } else { + trigger_error('Error: Could not load template ' . DIR_TEMPLATE . $this->template . '!'); + exit(); + } + } + + protected function out ($commonChildren = true) { + if($commonChildren === true){ + $this->children = array_merge(array( + 'common/footer', + 'common/header'), $this->children + ); + } + + return $this->response->setOutput($this->render()); + } +} +?> \ No newline at end of file diff --git a/system/engine/document.php b/system/engine/document.php new file mode 100644 index 0000000..6509c30 --- /dev/null +++ b/system/engine/document.php @@ -0,0 +1,185 @@ +title = sprintf($title, PATTERSITETITLE); + } else { + $this->title = $title; + } + } + + public function getTitle() { + return $this->title; + } + + public function setDescription($description) { + $this->description = $description; + } + + public function getDescription() { + return $this->description; + } + + public function setKeywords($keywords) { + $this->keywords = $keywords; + } + + public function getKeywords() { + return $this->keywords; + } + + public function addLink($href, $rel) { + $this->links[md5($href)] = array( + 'href' => $href, + 'rel' => $rel + ); + } + + public function getLinks() { + return $this->links; + } + + private function checkCDN($var) { + + if(defined('CDN')) { + if($this->checkLocal($var)){ + $var = CDN.$var; + } + } + + return $var; + + } + + public function addStyle($href, $rel = 'stylesheet', $media = 'screen', $minify = true) { + + if ($minify) $href = $this->cacheMinify($href, 'css'); + + $href = $this->checkCDN($href); + + $this->styles[md5($href)] = array( + 'href' => $href, + 'rel' => $rel, + 'media' => $media + ); + } + + public function getStyles() { + return $this->styles; + } + + public function addScript($script, $sort = '0', $minify = true) { + if($minify) $script = $this->cacheMinify($script, 'js'); + $script = $this->checkCDN($script); + $this->scripts[($sort)][md5($script)] = $script; + } + + public function getScripts() { + $a = $this->scripts; + ksort($a); + foreach($a as $value){ + foreach($value as $key => $value){ + $b[$key] = $value; + } + } + return $b; + } + + public function addFBMeta($property, $content = ''){ + $this->fbmetas[md5($property)] = array( + 'property' => $property, + 'content' => $content + ); + } + + public function getFBMetas(){ + return $this->fbmetas; + } + + private function checkLocal ($val) { + $testaProtocolo = substr($val, 0, 7); + + if($testaProtocolo != "http://" && $testaProtocolo != "https:/"){ + return true; + } else { + return false; + } + + } + + private function cacheMinify($ref, $type) { + + $dir = "css-js-cache/"; + $dirCache = DIR_PUBLIC. $dir; + $newName = str_replace("/", "_", $ref); + $file = DIR_PUBLIC.$ref; + $cachedFile = $dirCache.$newName; + $cacheFile = $dir.$newName; + + if(!$this->checkLocal($ref)) { + return $ref; + } + + if (!file_exists($dirCache)) { + mkdir($dirCache, 0755, true); + } + + if (file_exists($file) and defined('CACHE_MINIFY') and CACHE_MINIFY == true) { + if($type == "js") { + if(file_exists($cachedFile) and defined('CACHE_JS_CSS') and CACHE_JS_CSS == true) { + return $cacheFile; + } else { + + include_once DIR_SYSTEM."ecompress/JSMin.php"; + + $buffer = file_get_contents($file); + + $buffer = preg_replace('//Uis', '', $buffer); + + $buffer = JSMin::minify($buffer); + + file_put_contents($cachedFile, $buffer); + + return $cacheFile; + + } + + + }elseif($type == "css") { + if(file_exists($cachedFile) and defined('CACHE_JS_CSS') and CACHE_JS_CSS == true) { + return $cacheFile; + } else { + + include_once DIR_SYSTEM."ecompress/cssMin.php"; + + $buffer = file_get_contents($file); + + $buffer = minimizeCSS($buffer); + + file_put_contents($cachedFile, $buffer); + + return $cacheFile; + + } + + } else { + return $ref; + } + } else { + return $ref; + } + + + } + + +} +?> \ No newline at end of file diff --git a/system/engine/front.php b/system/engine/front.php new file mode 100644 index 0000000..a807618 --- /dev/null +++ b/system/engine/front.php @@ -0,0 +1,62 @@ +registry = $registry; + } + + public function addPreAction($pre_action) { + $this->pre_action[] = $pre_action; + } + + public function dispatch($action, $error) { + $this->error = $error; + + foreach ($this->pre_action as $pre_action) { + $result = $this->execute($pre_action); + + if ($result) { + $action = $result; + + break; + } + } + + while ($action) { + $action = $this->execute($action); + } + } + + private function execute($action) { + $file = $action->getFile(); + $class = $action->getClass(); + $method = $action->getMethod(); + $args = $action->getArgs(); + + $action = ''; + + if (file_exists($file)) { + require_once($file); + + $controller = new $class($this->registry); + + if (is_callable(array($controller, $method))) { + $action = call_user_func_array(array($controller, $method), $args); + } else { + $action = $this->error; + + $this->error = ''; + } + } else { + $action = $this->error; + + $this->error = ''; + } + + return $action; + } +} +?> \ No newline at end of file diff --git a/system/engine/loader.php b/system/engine/loader.php new file mode 100644 index 0000000..7c9a440 --- /dev/null +++ b/system/engine/loader.php @@ -0,0 +1,78 @@ +registry = $registry; + } + + public function __get($key) { + return $this->registry->get($key); + } + + public function __set($key, $value) { + $this->registry->set($key, $value); + } + + public function library($library) { + $file = DIR_SYSTEM . 'library/' . $library . '.php'; + + if (file_exists($file)) { + return include_once($file); + } else { + return trigger_error('Error: Could not load library ' . $library . '!'); + exit(); + } + } + + public function model($model) { + $file = DIR_APPLICATION . 'model/' . $model . '.php'; + $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', $model); + + if (file_exists($file)) { + include_once($file); + + $this->registry->set('model_' . str_replace('/', '_', $model), new $class($this->registry)); + } else { + trigger_error('Error: Could not load model ' . $model . '!'); + exit(); + } + } + + public function control($model) { + $file = DIR_APPLICATION . 'controller/' . $model . '.php'; + $class = 'Controller' . preg_replace('/[^a-zA-Z0-9]/', '', $model); + + if (file_exists($file)) { + include_once($file); + + $this->registry->set('controller_' . str_replace('/', '_', $model), new $class($this->registry)); + } else { + trigger_error('Error: Could not load model ' . $model . '!'); + exit(); + } + } + + public function database($driver, $hostname, $username, $password, $database, $prefix = NULL, $charset = 'UTF8') { + $file = DIR_SYSTEM . 'database/' . $driver . '.php'; + $class = 'Database' . preg_replace('/[^a-zA-Z0-9]/', '', $driver); + + if (file_exists($file)) { + include_once($file); + + $this->registry->set(str_replace('/', '_', $driver), new $class()); + } else { + trigger_error('Error: Could not load database ' . $driver . '!'); + exit(); + } + } + + public function config($config) { + $this->config->load($config); + } + + public function language($language) { + return $this->language->load($language); + } +} +?> \ No newline at end of file diff --git a/system/engine/log.php b/system/engine/log.php new file mode 100644 index 0000000..a93e23c --- /dev/null +++ b/system/engine/log.php @@ -0,0 +1,17 @@ +filename = fopen(DIR_LOGS . $filename, 'a'); + } + + public function write($message) { + fwrite($this->filename, date('Y-m-d G:i:s') . ' - ' . print_r($message, true)." | ".$_SERVER['REQUEST_URI'] . PHP_EOL); + } + + public function __destruct() { + fclose($this->filename); + } +} +?> \ No newline at end of file diff --git a/system/engine/model.php b/system/engine/model.php new file mode 100644 index 0000000..5f9eaec --- /dev/null +++ b/system/engine/model.php @@ -0,0 +1,17 @@ +registry = $registry; + } + + public function __get($key) { + return $this->registry->get($key); + } + + public function __set($key, $value) { + $this->registry->set($key, $value); + } +} +?> \ No newline at end of file diff --git a/system/engine/registry.php b/system/engine/registry.php new file mode 100644 index 0000000..8c3a816 --- /dev/null +++ b/system/engine/registry.php @@ -0,0 +1,17 @@ +data[$key]) ? $this->data[$key] : NULL); + } + + public function set($key, $value) { + $this->data[$key] = $value; + } + + public function has($key) { + return isset($this->data[$key]); + } +} +?> \ No newline at end of file diff --git a/system/engine/response.php b/system/engine/response.php new file mode 100644 index 0000000..0f56875 --- /dev/null +++ b/system/engine/response.php @@ -0,0 +1,72 @@ +headers[] = $header; + } + + public function redirect($url) { + header('Location: ' . $url); + exit; + } + + public function setCompression($level) { + $this->level = $level; + } + + public function setOutput($output) { + $this->output = $output; + } + + private function compress($data, $level = 0) { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false)) { + $encoding = 'gzip'; + } + + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false)) { + $encoding = 'x-gzip'; + } + + if (!isset($encoding)) { + return $data; + } + + if (!extension_loaded('zlib') || ini_get('zlib.output_compression')) { + return $data; + } + + if (headers_sent()) { + return $data; + } + + if (connection_status()) { + return $data; + } + + $this->addHeader('Content-Encoding: ' . $encoding); + + return gzencode($data, (int)$level); + } + + public function output() { + if ($this->output) { + if ($this->level) { + $ouput = $this->compress($this->output, $this->level); + } else { + $ouput = $this->output; + } + + if (!headers_sent()) { + foreach ($this->headers as $header) { + header($header, true); + } + } + + echo $ouput; + } + } +} +?> \ No newline at end of file diff --git a/system/image/autoload.php b/system/image/autoload.php new file mode 100644 index 0000000..08b0871 --- /dev/null +++ b/system/image/autoload.php @@ -0,0 +1,183 @@ +file = $file; + + $info = getimagesize($file); + + $this->info = array( + 'width' => $info[0], + 'height' => $info[1], + 'bits' => $info['bits'], + 'mime' => $info['mime'] + ); + + $this->image = $this->create($file); + } else { + exit('Error: Could not load image ' . $file . '!'); + } + } + + private function create($image) { + $mime = $this->info['mime']; + + if ($mime == 'image/gif') { + return imagecreatefromgif($image); + } elseif ($mime == 'image/png') { + return imagecreatefrompng($image); + } elseif ($mime == 'image/jpeg') { + return imagecreatefromjpeg($image); + } + } + + public function save($file, $quality = 90) { + $info = pathinfo($file); + + $extension = strtolower($info['extension']); + + if ($extension == 'jpeg' || $extension == 'jpg') { + imagejpeg($this->image, $file, $quality); + } elseif($extension == 'png') { + imagepng($this->image, $file, 0); + } elseif($extension == 'gif') { + imagegif($this->image, $file); + } + + imagedestroy($this->image); + } + + public function resize($width = 0, $height = 0) { + if (!$this->info['width'] || !$this->info['height']) { + return; + } + + $xpos = 0; + $ypos = 0; + + $scale = min($width / $this->info['width'], $height / $this->info['height']); + + if ($scale == 1) { + return; + } + + $new_width = (int)($this->info['width'] * $scale); + $new_height = (int)($this->info['height'] * $scale); + $xpos = (int)(($width - $new_width) / 2); + $ypos = (int)(($height - $new_height) / 2); + + $image_old = $this->image; + $this->image = imagecreatetruecolor($width, $height); + + if (isset($this->info['mime']) && $this->info['mime'] == 'image/png') { + imagealphablending($this->image, false); + imagesavealpha($this->image, true); + $background = imagecolorallocatealpha($this->image, 255, 255, 255, 127); + imagecolortransparent($this->image, $background); + } else { + $background = imagecolorallocate($this->image, 255, 255, 255); + } + + imagefilledrectangle($this->image, 0, 0, $width, $height, $background); + + imagecopyresampled($this->image, $image_old, $xpos, $ypos, 0, 0, $new_width, $new_height, $this->info['width'], $this->info['height']); + imagedestroy($image_old); + + $this->info['width'] = $width; + $this->info['height'] = $height; + } + + public function watermark($file, $position = 'bottomright') { + $watermark = $this->create($file); + + $watermark_width = imagesx($watermark); + $watermark_height = imagesy($watermark); + + switch($position) { + case 'topleft': + $watermark_pos_x = 0; + $watermark_pos_y = 0; + break; + case 'topright': + $watermark_pos_x = $this->info['width'] - $watermark_width; + $watermark_pos_y = 0; + break; + case 'bottomleft': + $watermark_pos_x = 0; + $watermark_pos_y = $this->info['height'] - $watermark_height; + break; + case 'bottomright': + $watermark_pos_x = $this->info['width'] - $watermark_width; + $watermark_pos_y = $this->info['height'] - $watermark_height; + break; + } + + imagecopy($this->image, $watermark, $watermark_pos_x, $watermark_pos_y, 0, 0, 120, 40); + + imagedestroy($watermark); + } + + public function crop($top_x, $top_y, $bottom_x, $bottom_y) { + $image_old = $this->image; + $this->image = imagecreatetruecolor($bottom_x - $top_x, $bottom_y - $top_y); + + imagecopy($this->image, $image_old, 0, 0, $top_x, $top_y, $this->info['width'], $this->info['height']); + imagedestroy($image_old); + + $this->info['width'] = $bottom_x - $top_x; + $this->info['height'] = $bottom_y - $top_y; + } + + public function rotate($degree, $color = 'FFFFFF') { + $rgb = $this->html2rgb($color); + + $this->image = imagerotate($this->image, $degree, imagecolorallocate($this->image, $rgb[0], $rgb[1], $rgb[2])); + + $this->info['width'] = imagesx($this->image); + $this->info['height'] = imagesy($this->image); + } + + private function filter($filter) { + imagefilter($this->image, $filter); + } + + private function text($text, $x = 0, $y = 0, $size = 5, $color = '000000') { + $rgb = $this->html2rgb($color); + + imagestring($this->image, $size, $x, $y, $text, imagecolorallocate($this->image, $rgb[0], $rgb[1], $rgb[2])); + } + + private function merge($file, $x = 0, $y = 0, $opacity = 100) { + $merge = $this->create($file); + + $merge_width = imagesx($image); + $merge_height = imagesy($image); + + imagecopymerge($this->image, $merge, $x, $y, 0, 0, $merge_width, $merge_height, $opacity); + } + + private function html2rgb($color) { + if ($color[0] == '#') { + $color = substr($color, 1); + } + + if (strlen($color) == 6) { + list($r, $g, $b) = array($color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5]); + } elseif (strlen($color) == 3) { + list($r, $g, $b) = array($color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2]); + } else { + return false; + } + + $r = hexdec($r); + $g = hexdec($g); + $b = hexdec($b); + + return array($r, $g, $b); + } +} +?> \ No newline at end of file diff --git a/system/library/encryption.php b/system/library/encryption.php new file mode 100644 index 0000000..b6bd4e6 --- /dev/null +++ b/system/library/encryption.php @@ -0,0 +1,109 @@ +key = $this->hash($key); + + if(function_exists('openssl_encrypt')) { + $this->method = 'openssl'; + $this->cipher = $opensslCipher; + } else { + $this->method = 'base64'; + } + + } + + public function hash($value) { + return hash('sha256', $value, true); + } + + public function encrypt ($value, $key = NULL) { + $this->key = ($key != NULL) ? $key : $this->key; + + if($this->method == 'openssl') { + return $this->opensslEncrypt($value); + } else { + return $this->base64Encrypt($value); + } + } + + public function decrypt ($value, $key = NULL) { + $this->key = ($key != NULL) ? $key : $this->key; + + if($this->method == 'openssl') { + return $this->opensslDecrypt($value); + } else { + return $this->base64Decrypt($value); + } + } + + function base64Encrypt($value) { + if (!$this->key) { + return $value; + } + + $output = ''; + + for ($i = 0; $i < strlen($value); $i++) { + $char = substr($value, $i, 1); + $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1); + $char = chr(ord($char) + ord($keychar)); + + $output .= $char; + } + + return base64_encode($output); + } + + function base64Decrypt($value) { + if (!$this->key) { + return $value; + } + + $output = ''; + + $value = base64_decode($value); + + for ($i = 0; $i < strlen($value); $i++) { + $char = substr($value, $i, 1); + $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1); + $char = chr(ord($char) - ord($keychar)); + + $output .= $char; + } + + return $output; + } + + private function opensslEncrypt ($value) { + + $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cipher)); + + $ciphertext_raw = strtr(base64_encode(openssl_encrypt($value, $this->cipher, $this->hash( $this->key), 0, $iv)), '+/=', '-_,'); + + //$hmac = hash_hmac('sha256', $ciphertext_raw, $this->key, true); + + $output = $this->base64Encrypt( $iv.$ciphertext_raw ); + + return $output; + + } + + private function opensslDecrypt ($value) { + + $c = $this->base64Decrypt($value); + $ivlen = openssl_cipher_iv_length($this->cipher); + $iv = substr($c, 0, $ivlen); + //$hmac = substr($c, $ivlen, $sha2len=32); + $ciphertext_raw = substr($c, $ivlen); + + + $output = trim(openssl_decrypt(base64_decode(strtr($ciphertext_raw, '-_,', '+/=')), $this->cipher, $this->hash($this->key), 0, $iv)); + + return $output; + } +} +?> \ No newline at end of file diff --git a/system/login/autoload.php b/system/login/autoload.php new file mode 100644 index 0000000..3d2c975 --- /dev/null +++ b/system/login/autoload.php @@ -0,0 +1,81 @@ +MM_authorizedUsers = $authorizedUsers; + $this->request = new Request(); + $this->session = new Session(); + + } + + // *** Restrict Access To Page: Grant or deny access to this page + public function isAuthorized($strUsers, $strGroups, $UserName, $UserGroup) { + + // For security, start by assuming the visitor is NOT authorized. + $isValid = False; + + // When a visitor has logged into this site, the Session variable MM_Username set equal to their username. + // Therefore, we know that a user is NOT logged in if that Session variable is blank. + if (!empty($UserName)) { + // Besides being logged in, you may restrict access to only certain users based on an ID established when they login. + // Parse the strings into arrays. + $arrUsers = Explode(",", $strUsers); + $arrGroups = Explode(",", $strGroups); + if (in_array($UserName, $arrUsers)) { + $isValid = true; + } + // Or, you may restrict access to only certain users based on their username. + if (in_array($UserGroup, $arrGroups)) { + $isValid = true; + } + if (($strUsers == "") && false) { + $isValid = true; + } + } + return $isValid; + } + + public function check($restrictGoTo) { + + $MM_restrictGoTo = $restrictGoTo; + + if (!((isset($this->session->data['MM_Username'])) && ($this->isAuthorized("",$this->MM_authorizedUsers, $this->session->data['MM_Username'], $this->session->data['MM_UserGroup'])))) { + $MM_qsChar = "?"; + $MM_referrer = $this->request->server['PHP_SELF']; + if (strpos($MM_restrictGoTo, "?")) $MM_qsChar = "&"; + if (isset($this->request->server['QUERY_STRING']) && strlen($this->request->server['QUERY_STRING']) > 0) + $MM_referrer .= "?" . $this->request->server['QUERY_STRING']; + $MM_restrictGoTo = $MM_restrictGoTo. $MM_qsChar . "accesscheck=" . urlencode($MM_referrer); + header("Location: ". $MM_restrictGoTo); + exit; + } + } + + public function isLogged () { + $lgged = $this->isAuthorized("",$this->MM_authorizedUsers, $this->session->data['MM_Username'], $this->session->data['MM_UserGroup']); + + return($lgged); + } + + public function logout() { + unset($this->session->data['user_id']); + + $this->user_id = ''; + $this->username = ''; + + session_destroy(); + } + + public function getUserName() { + return $this->session->data['MM_Username']; + } + + + +} \ No newline at end of file diff --git a/system/mail/autoload.php b/system/mail/autoload.php new file mode 100644 index 0000000..4bbc524 --- /dev/null +++ b/system/mail/autoload.php @@ -0,0 +1,3 @@ +to = $to; + } + + public function setFrom($from) { + $this->from = $from; + } + + public function setSender($sender) { + $this->sender = html_entity_decode($sender, ENT_QUOTES, 'UTF-8'); + } + + public function setSubject($subject) { + $this->subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8'); + } + + public function setText($text) { + $this->text = $text; + } + + public function setHtml($html) { + $this->html = $html; + } + + public function addAttachment($file, $filename = '') { + if (!$filename) { + $filename = basename($file); + } + + $this->attachments[] = array( + 'filename' => $filename, + 'file' => $file + ); + } + + public function send() { + if (!$this->to) { + trigger_error('Error: E-Mail to required!'); + exit(); + } + + if (!$this->from) { + trigger_error('Error: E-Mail from required!'); + exit(); + } + + if (!$this->sender) { + trigger_error('Error: E-Mail sender required!'); + exit(); + } + + if (!$this->subject) { + trigger_error('Error: E-Mail subject required!'); + exit(); + } + + if ((!$this->text) && (!$this->html)) { + trigger_error('Error: E-Mail message required!'); + exit(); + } + + if (is_array($this->to)) { + $to = implode(',', $this->to); + } else { + $to = $this->to; + } + + $boundary = '----=_NextPart_' . md5(time()); + + $header = ''; + + $header .= 'MIME-Version: 1.0' . $this->newline; + + if ($this->protocol != 'mail') { + $header .= 'To: ' . $to . $this->newline; + $header .= 'Subject: ' . $this->subject . $this->newline; + } + + $header .= 'Date: ' . date("D, d M Y H:i:s O") . $this->newline; + $header .= 'From: ' . '=?UTF-8?B?' . base64_encode($this->sender) . '?=' . '<' . $this->from . '>' . $this->newline; + $header .= 'Reply-To: ' . $this->sender . '<' . $this->from . '>' . $this->newline; + $header .= 'Return-Path: ' . $this->from . $this->newline; + $header .= 'X-Mailer: PHP/' . phpversion() . $this->newline; + $header .= 'Content-Type: multipart/related; boundary="' . $boundary . '"' . $this->newline; + + if (!$this->html) { + $message = '--' . $boundary . $this->newline; + $message .= 'Content-Type: text/plain; charset="utf-8"' . $this->newline; + $message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline; + $message .= $this->text . $this->newline; + } else { + $message = '--' . $boundary . $this->newline; + $message .= 'Content-Type: multipart/alternative; boundary="' . $boundary . '_alt"' . $this->newline . $this->newline; + $message .= '--' . $boundary . '_alt' . $this->newline; + $message .= 'Content-Type: text/plain; charset="utf-8"' . $this->newline; + $message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline; + + if ($this->text) { + $message .= $this->text . $this->newline; + } else { + $message .= 'This is a HTML email and your email client software does not support HTML email!' . $this->newline; + } + + $message .= '--' . $boundary . '_alt' . $this->newline; + $message .= 'Content-Type: text/html; charset="utf-8"' . $this->newline; + $message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline; + $message .= $this->html . $this->newline; + $message .= '--' . $boundary . '_alt--' . $this->newline; + } + + foreach ($this->attachments as $attachment) { + if (file_exists($attachment['file'])) { + $handle = fopen($attachment['file'], 'r'); + + $content = fread($handle, filesize($attachment['file'])); + + fclose($handle); + + $message .= '--' . $boundary . $this->newline; + $message .= 'Content-Type: application/octetstream; name="' . basename($attachment['file']) . '"' . $this->newline; + $message .= 'Content-Transfer-Encoding: base64' . $this->newline; + $message .= 'Content-Disposition: attachment; filename="' . basename($attachment['filename']) . '"' . $this->newline; + $message .= 'Content-ID: <' . basename($attachment['filename']) . '>' . $this->newline; + $message .= 'X-Attachment-Id: ' . basename($attachment['filename']) . $this->newline . $this->newline; + $message .= chunk_split(base64_encode($content)); + } + } + + $message .= '--' . $boundary . '--' . $this->newline; + + if ($this->protocol == 'mail') { + ini_set('sendmail_from', $this->from); + + if ($this->parameter) { + mail($to, '=?UTF-8?B?' . base64_encode($this->subject) . '?=', $message, $header, $this->parameter); + } else { + mail($to, '=?UTF-8?B?' . base64_encode($this->subject) . '?=', $message, $header); + } + + } elseif ($this->protocol == 'smtp') { + $handle = fsockopen($this->hostname, $this->port, $errno, $errstr, $this->timeout); + + if (!$handle) { + trigger_error('Error: ' . $errstr . ' (' . $errno . ')'); + exit(); + } else { + if (substr(PHP_OS, 0, 3) != 'WIN') { + socket_set_timeout($handle, $this->timeout, 0); + } + + while ($line = fgets($handle, 515)) { + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($this->hostname, 0, 3) == 'tls') { + fputs($handle, 'STARTTLS' . $this->crlf); + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 220) { + trigger_error('Error: STARTTLS not accepted from server!'); + exit(); + } + } + + if (!empty($this->username) && !empty($this->password)) { + fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 250) { + trigger_error('Error: EHLO not accepted from server!'); + exit(); + } + + fputs($handle, 'AUTH LOGIN' . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 334) { + trigger_error('Error: AUTH LOGIN not accepted from server!'); + exit(); + } + + fputs($handle, base64_encode($this->username) . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 334) { + trigger_error('Error: Username not accepted from server!'); + exit(); + } + + fputs($handle, base64_encode($this->password) . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 235) { + trigger_error('Error: Password not accepted from server!'); + exit(); + } + } else { + fputs($handle, 'HELO ' . getenv('SERVER_NAME') . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 250) { + trigger_error('Error: HELO not accepted from server!'); + exit(); + } + } + + if ($this->verp) { + fputs($handle, 'MAIL FROM: <' . $this->from . '>XVERP' . $this->crlf); + } else { + fputs($handle, 'MAIL FROM: <' . $this->from . '>' . $this->crlf); + } + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 250) { + trigger_error('Error: MAIL FROM not accepted from server!'); + exit(); + } + + if (!is_array($this->to)) { + fputs($handle, 'RCPT TO: <' . $this->to . '>' . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) { + trigger_error('Error: RCPT TO not accepted from server!'); + exit(); + } + } else { + foreach ($this->to as $recipient) { + fputs($handle, 'RCPT TO: <' . $recipient . '>' . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) { + trigger_error('Error: RCPT TO not accepted from server!'); + exit(); + } + } + } + + fputs($handle, 'DATA' . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 354) { + trigger_error('Error: DATA not accepted from server!'); + exit(); + } + + // According to rfc 821 we should not send more than 1000 including the CRLF + $message = str_replace("\r\n", "\n", $header . $message); + $message = str_replace("\r", "\n", $message); + + $lines = explode("\n", $message); + + foreach ($lines as $line) { + $results = str_split($line, 998); + + foreach ($results as $result) { + if (substr(PHP_OS, 0, 3) != 'WIN') { + fputs($handle, $result . $this->crlf); + } else { + fputs($handle, str_replace("\n", "\r\n", $result) . $this->crlf); + } + } + } + + fputs($handle, '.' . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 250) { + trigger_error('Error: DATA not accepted from server!'); + exit(); + } + + fputs($handle, 'QUIT' . $this->crlf); + + $reply = ''; + + while ($line = fgets($handle, 515)) { + $reply .= $line; + + if (substr($line, 3, 1) == ' ') { + break; + } + } + + if (substr($reply, 0, 3) != 221) { + trigger_error('Error: QUIT not accepted from server!'); + exit(); + } + + fclose($handle); + } + } + } +} +?> \ No newline at end of file diff --git a/system/pagination/autoload.php b/system/pagination/autoload.php new file mode 100644 index 0000000..3d66465 --- /dev/null +++ b/system/pagination/autoload.php @@ -0,0 +1,100 @@ +'', 'end'=>''); + public $output_html = array('begin'=>'', 'end'=>''); + public $no_link_html = array('begin'=>'', 'end'=>''); + + public function render() { + $total = $this->total; + + if ($this->page < 1) { + $page = 1; + } else { + $page = $this->page; + } + + if (!$this->limit) { + $limit = 10; + } else { + $limit = $this->limit; + } + + $num_links = $this->num_links; + $num_pages = ceil($total / $limit); + + $output = ''; + + if ($page > 1) { + $output .= $this->links_html['begin'].' ' . $this->text_first . ' '.$this->links_html['end'].$this->links_html['begin'].'' . $this->text_prev . ' '.$this->links_html['end']; + } + + if ($num_pages > 1) { + if ($num_pages <= $num_links) { + $start = 1; + $end = $num_pages; + } else { + $start = $page - floor($num_links / 2); + $end = $page + floor($num_links / 2); + + if ($start < 1) { + $end += abs($start) + 1; + $start = 1; + } + + if ($end > $num_pages) { + $start -= ($end - $num_pages); + $end = $num_pages; + } + } + + if ($start > 1) { + $output .= $this->no_link_html['begin'].' ... '.$this->no_link_html['end']; + } + + for ($i = $start; $i <= $end; $i++) { + if ($page == $i) { + $output .= $this->no_link_html['begin'].' ' . $i . ' '.$this->no_link_html['end']; + } else { + $output .= $this->links_html['begin'].' ' . $i . ' '.$this->links_html['end']; + } + } + + if ($end < $num_pages) { + $output .= $this->no_link_html['begin'].' ... '.$this->no_link_html['end']; + } + } + + if ($page < $num_pages) { + $output .= $this->links_html['begin'].' ' . $this->text_next . ''.$this->links_html['end'].$this->links_html['begin'].' ' . $this->text_last . ' '.$this->links_html['end']; + } + + $find = array( + '{start}', + '{end}', + '{total}', + '{pages}' + ); + + $replace = array( + ($total) ? (($page - 1) * $limit) + 1 : 0, + ((($page - 1) * $limit) > ($total - $limit)) ? $total : ((($page - 1) * $limit) + $limit), + $total, + $num_pages + ); + + return ($output ? '' : '') . '
    ' . str_replace($find, $replace, $this->text) . '
    '; + } +} +?> \ No newline at end of file diff --git a/system/registrations.php b/system/registrations.php new file mode 100644 index 0000000..a1bc86e --- /dev/null +++ b/system/registrations.php @@ -0,0 +1,2 @@ +clean($_GET); + $_POST = $this->clean($_POST); + $_REQUEST = $this->clean($_REQUEST); + $_COOKIE = $this->clean($_COOKIE); + $_FILES = $this->clean($_FILES); + $_SERVER = $this->clean($_SERVER); + + $this->get = $_GET; + $this->post = $_POST; + $this->request = $_REQUEST; + $this->cookie = $_COOKIE; + $this->files = $_FILES; + $this->server = $_SERVER; + } + + public function clean($data) { + if (is_array($data)) { + foreach ($data as $key => $value) { + unset($data[$key]); + + $data[$this->clean($key)] = $this->clean($value); + } + } else { + $data = htmlspecialchars($data, ENT_COMPAT); + } + + return $data; + } +} +?> \ No newline at end of file diff --git a/system/session/autoload.php b/system/session/autoload.php new file mode 100644 index 0000000..2e3a105 --- /dev/null +++ b/system/session/autoload.php @@ -0,0 +1,17 @@ +data =& $_SESSION; + } +} +?> \ No newline at end of file diff --git a/system/system.php b/system/system.php new file mode 100644 index 0000000..961ff9c --- /dev/null +++ b/system/system.php @@ -0,0 +1,253 @@ +constants = get_defined_constants(true); + $this->userConstants = $this->constants['user']; + $this->includes = get_included_files(); + + // Check Version + $this->phpversion = $this->checkPHPversion(); + + //Check Config Load + $loadConfig = $this->checkConfigFile(); + + if($loadConfig) { + $this->defineAuxConstants(); + } + + if(defined('DEBUG') && DEBUG == true) { + error_reporting(E_ALL); + ini_set('display_errors', 1); + } + + $this->loadengine(); + } + + private function checkPHPversion() { + if (version_compare(phpversion(), '5.3.0', '>') == false) { + exit('PHP 5.3+ Required'); + } else { + return phpversion(); + } + } + + private function checkConfigFile() { + + if (!$this->checkConstantsRequired()) { + try { + $baseDir = str_replace('system', '', __DIR__); + include_once($baseDir."config.php"); + + if (!$this->checkConstantsRequired()) { + throw new \Exception("Can't load minimun config constants, please check your config file!"); + } + + } catch (Exception $e) { + exit($e->getMessage()); + } + + } else { + return true; + } + + } + + private function checkConstantsRequired () { + if (!defined('DIR_APPLICATION') || !defined('DIR_SYSTEM') || !defined('DIR_PUBLIC') || !defined('DIR_TEMPLATE') || !defined('USE_DB_CONFIG')) { + return(false); + } else { + return(true); + } + } + + private function defineAuxConstants () { + (defined('HTTP_URL')) ? define('HTTP_SERVER', HTTP_URL) : ''; + (defined('HTTPS_URL')) ? define('HTTPS_SERVER', HTTPS_URL) : ''; + } + + private function loadengine () { + $this->dirs = glob(DIR_SYSTEM.'*/autoload.php', GLOB_BRACE); + foreach($this->dirs as $key => $value) { + if($value != ""){ + try { + if(is_readable($value)) { + require_once $value; + } else { + throw new \Exception("I can't load '$value' file! Please check system permissions."); + } + } catch (Exception $e) { + exit($e->getMessage()); + } + + } + } + } + +} + +$engine = new startEngineExacTI(); + +// Registry +$registry = new Registry(); +$registry->set('engine', $engine); + +// Loader +$loader = new Loader($registry); +$registry->set('load', $loader); + +// Config +$config = new Config(); +$registry->set('config', $config); + +$registry->set('db', $db); + +// Settings +if(!empty($configs)){ + foreach ($configs as $key => $confValue) { + $config->set($key, $confValue); + } +} + +if(USE_DB_CONFIG === true) { + + $query = $db->query("SELECT * FROM settings ORDER BY store_id DESC, setting_id ASC"); + + foreach ($query->rows as $setting) { + if (!$setting['serialized']) { + $config->set($setting['key'], $setting['value']); + } else { + $config->set($setting['key'], unserialize($setting['value'])); + } + } +} + + +$config->set('config_url', HTTP_URL); +$config->set('config_ssl', HTTPS_URL); + +// Site Title +if($config->get('PatternSiteTitle') == true) { + define('PATTERSITETITLE', $config->get('PatternSiteTitle')); +} else { + define('PATTERSITETITLE', false); +} + +// Url +$url = new Url($config->get('config_url'), $config->get('config_use_ssl') ? $config->get('config_ssl') : $config->get('config_url')); +$registry->set('url', $url); + +// Log +if(!$config->get('config_error_filename')){ + $config->set('config_error_filename', 'error.log'); +} + +$log = new Log($config->get('config_error_filename')); +$registry->set('log', $log); + +function error_handler($errno, $errstr, $errfile, $errline) { + global $log, $config; + + switch ($errno) { + case E_NOTICE: + case E_USER_NOTICE: + $error = 'Notice'; + break; + case E_WARNING: + case E_USER_WARNING: + $error = 'Warning'; + break; + case E_ERROR: + case E_USER_ERROR: + $error = 'Fatal Error'; + break; + default: + $error = 'Unknown'; + break; + } + + if ($config->get('config_error_display')) { + echo '' . $error . ': ' . $errstr . ' in ' . $errfile . ' on line ' . $errline . ''; + } + + if ($config->get('config_error_log')) { + $log->write('PHP ' . $error . ': ' . $errstr . ' in ' . $errfile . ' on line ' . $errline); + } + + return true; +} + +// Error Handler +set_error_handler('error_handler'); + +//Caches +$caches = new Caches(); +$registry->set('cache', $caches); + +// Request +$request = new Request(); +$registry->set('request', $request); + +// Response +$response = new Response(); +$response->addHeader('Content-Type: text/html; charset=utf-8'); +$response->setCompression($config->get('config_compression')); +$registry->set('response', $response); + +// Session +$session = new Session(); +$registry->set('session', $session); + +// Translate +$translate = new Translate(); +$registry->set('translate', $translate); + +// E-Mail Config +$mail = new Mail(); +$mail->protocol = $config->get('config_mail_protocol'); +if($config->get('config_mail_protocol') == 'smtp'){ + $mail->parameter = $config->get('config_mail_parameter'); + $mail->hostname = $config->get('config_smtp_host'); + $mail->username = $config->get('config_smtp_username'); + $mail->password = $config->get('config_smtp_password'); + $mail->port = $config->get('config_smtp_port'); + $mail->timeout = $config->get('config_smtp_timeout'); +} +$registry->set('mail', $mail); + +// Document +$document = new Document(); +$registry->set('document', $document); + +// Personalized registrations +include(DIR_SYSTEM."registrations.php"); + +// Front Controller +$controller = new Front($registry); + +// SEO URL's +$controller->addPreAction(new ActionSystem('url/seo_url')); + +// Router +if (isset($request->get['route'])) { + $action = new Action($request->get['route']); +} else { + $request->get['route'] = "common/home"; + $action = new Action('common/home'); +} + +// Dispatch +$controller->dispatch($action, new Action('error/not_found')); + +// Output +$response->output(); + +//var_dump(new ActionSystem('url/seo_url')); + +?> \ No newline at end of file diff --git a/system/templateEngines/Dwoo/autoload.php b/system/templateEngines/Dwoo/autoload.php new file mode 100644 index 0000000..bcdae55 --- /dev/null +++ b/system/templateEngines/Dwoo/autoload.php @@ -0,0 +1,8 @@ + + * Jordi Boggiano + * + * 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 + * @author Jordi Boggiano + * @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') && ini_get('apc.enabled') ? $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; +} diff --git a/system/templateEngines/Dwoo/composer/LICENSE b/system/templateEngines/Dwoo/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/system/templateEngines/Dwoo/composer/LICENSE @@ -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. + diff --git a/system/templateEngines/Dwoo/composer/autoload_classmap.php b/system/templateEngines/Dwoo/composer/autoload_classmap.php new file mode 100644 index 0000000..7a91153 --- /dev/null +++ b/system/templateEngines/Dwoo/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + array($vendorDir . '/dwoo/dwoo/lib/Dwoo'), +); diff --git a/system/templateEngines/Dwoo/composer/autoload_real.php b/system/templateEngines/Dwoo/composer/autoload_real.php new file mode 100644 index 0000000..ddf32cc --- /dev/null +++ b/system/templateEngines/Dwoo/composer/autoload_real.php @@ -0,0 +1,52 @@ += 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\ComposerStaticInit301ac16403f2b6bfc7e114ab0baaa05c::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); + + return $loader; + } +} diff --git a/system/templateEngines/Dwoo/composer/autoload_static.php b/system/templateEngines/Dwoo/composer/autoload_static.php new file mode 100644 index 0000000..5ae368f --- /dev/null +++ b/system/templateEngines/Dwoo/composer/autoload_static.php @@ -0,0 +1,31 @@ + + array ( + 'Dwoo\\' => 5, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Dwoo\\' => + array ( + 0 => __DIR__ . '/..' . '/dwoo/dwoo/lib/Dwoo', + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit301ac16403f2b6bfc7e114ab0baaa05c::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit301ac16403f2b6bfc7e114ab0baaa05c::$prefixDirsPsr4; + + }, null, ClassLoader::class); + } +} diff --git a/system/templateEngines/Dwoo/composer/installed.json b/system/templateEngines/Dwoo/composer/installed.json new file mode 100644 index 0000000..48ae1e9 --- /dev/null +++ b/system/templateEngines/Dwoo/composer/installed.json @@ -0,0 +1,43 @@ +[ + { + "name": "dwoo/dwoo", + "version": "1.3.7", + "version_normalized": "1.3.7.0", + "source": { + "type": "git", + "url": "https://github.com/dwoo-project/dwoo.git", + "reference": "30592b34b38b1d50e3c7a88b8a5ce315d40ea96e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dwoo-project/dwoo/zipball/30592b34b38b1d50e3c7a88b8a5ce315d40ea96e", + "reference": "30592b34b38b1d50e3c7a88b8a5ce315d40ea96e", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "4.8.27" + }, + "time": "2018-04-18T14:25:28+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Dwoo\\": "lib/Dwoo/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-only" + ], + "authors": [ + { + "name": "David Sanchez", + "email": "david38sanchez@gmail.com" + } + ], + "description": "Dwoo is a PHP5/PHP7 template engine which is (almost) fully compatible with Smarty templates and plugins, but is written from scratch for PHP5, and adds many features." + } +] diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/.gitignore b/system/templateEngines/Dwoo/dwoo/dwoo/.gitignore new file mode 100644 index 0000000..a61249c --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/.gitignore @@ -0,0 +1,12 @@ + +### Composer template +composer.phar +/vendor/ + +# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +composer.lock + +### PHPUnit Tests +/tests/temp/cache/ +/tests/temp/compiled/ \ No newline at end of file diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/.gitlab-ci.yml b/system/templateEngines/Dwoo/dwoo/dwoo/.gitlab-ci.yml new file mode 100644 index 0000000..07c923a --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/.gitlab-ci.yml @@ -0,0 +1,77 @@ +# Before Script +before_script: + - apt-get update -yqq + - apt-get install git -yqq + +# Stages +stages: + - test + +# Image from https://github.com/splitbrain/docker-phpfarm +image: splitbrain/phpfarm:jessie + +# PHP5.3 +php:5.3: + stage: test + script: + - echo "Running PHPUnit Tests on PHP5.3" + - wget https://getcomposer.org/composer.phar -O composer + - php-5.3 composer install + - wget https://phar.phpunit.de/phpunit-old.phar -O phpunit + - php-5.3 phpunit -d memory_limit=512M --colors --debug --coverage-text + +# PHP5.4 +php:5.4: + stage: test + script: + - echo "Running PHPUnit Tests on PHP5.4" + - wget https://getcomposer.org/composer.phar -O composer + - php-5.4 composer install + - wget https://phar.phpunit.de/phpunit-old.phar -O phpunit + - php-5.4 phpunit -d memory_limit=512M --colors --debug --coverage-text + +# PHP5.5 +php:5.5: + stage: test + script: + - echo "Running PHPUnit Tests on PHP5.5" + - wget https://getcomposer.org/composer.phar -O composer + - php-5.5 composer install + - wget https://phar.phpunit.de/phpunit-old.phar -O phpunit + - php-5.5 phpunit -d memory_limit=512M --colors --debug --coverage-text + +# PHP5.6 +php:5.6: + image: php:5.6 + script: + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + - echo "Running PHPUnit Tests on PHP5.6" + - php vendor/bin/phpunit --colors --debug --coverage-text + +# PHP7.0 +php:7.0: + image: php:7.0 + script: + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + - echo "Running PHPUnit Tests on PHP7.0" + - php vendor/bin/phpunit --colors --debug --coverage-text + +# PHP7.1 +php:7.1: + image: php:7.1 + script: + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + - echo "Running PHPUnit Tests on PHP7.1" + - php vendor/bin/phpunit --colors --debug --coverage-text + +# PHP7.2 +php:7.2: + image: php:7.2 + script: + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + - echo "Running PHPUnit Tests on PHP7.2" + - php vendor/bin/phpunit --colors --debug --coverage-text \ No newline at end of file diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/.travis.yml b/system/templateEngines/Dwoo/dwoo/dwoo/.travis.yml new file mode 100644 index 0000000..f262fb0 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/.travis.yml @@ -0,0 +1,13 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + +before_script: + - composer install diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/CHANGELOG.md b/system/templateEngines/Dwoo/dwoo/dwoo/CHANGELOG.md new file mode 100644 index 0000000..dbdb9fc --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/CHANGELOG.md @@ -0,0 +1,770 @@ +## 1.3.6 (2017-03-27) +`Changed` +* In `setCacheDir` and `setCompileDir` methods, create directory path if not exist if possible. +* Rename `Compiler::getDwoo()` to `Compiler::getCore()`. + +`Fixed` +* Fix bug, using object for custom plugin and using `getobjectPlugin` method. +* Fix error, `getCore` need to return `$this->core`. +* Fix output data bug in `replaceModifiers` method. +* Fix `addPlugin` method, add code when passing object in `$callback`. + +## 1.3.5 (2017-03-16) +`Added` +* Add new constant test from file `testShortClassConstants`. + +`Changed` +* Update tests, add new test `testClassConstants`. +* Update `Loader::rebuildClassPathCache` method, using `DirectoryIterator` class instead of `glob` function. + +`Fixed` +* Fix class `BaseTests`, remove *cached* and *compiled* files when calling **destructor**. +* Constants error when using 2 trailing slashes, issue [#63](https://github.com/dwoo-project/dwoo/issues/63). +* Fix `parseConstKey` method, replacing double `\\` by single `\`. +* Fix `throw new` exception, display on one line. + +`Removed` +* Remove useless `else` and non accessing code. + +## 1.3.4 (2017-03-07) +`Added` +* Add `docker-compose.yml` file for unit testing only. + +`Changed` +* Update PHPUnit commands in *tests/README.md* file. +* Ignore `composer.lock` file. +* Update code `while (list(, $v) = each($args))` to `foreach ($args as $key => $v)`. + +`Fixed` +* Fixing issue [#58](https://github.com/dwoo-project/dwoo/issues/58). +* Update method `Dwoo\Template\File::getCompiledFilename`. + +## 1.3.3 (2017-01-09) +`Added` +* Add new parameter `allowed_tags` for **strip_tags** plugin. + +`Changed` +* All **function's plugins** as *PHP function* has been converted to *PHP class*, following now PSR-0 and PSR-4 standards. +* `array` helper as *PHP function* has been converted to *PHP class*, following now PSR-0 and PSR-4 standards. + +## 1.3.2 (2017-01-05) +`Added` +* Add new tests: `CoreTest::testSetters`, `CoreTest::testGlobal`. +* Add new methods: `Core::setTemplateDir` and `Core::getTemplateDir`. +* Add new alias `js` for *javascript* available format in **escape** plugin. +* Add new methods `Core::addGlobal` and `Core::getGlobals`. + +`Changed` +* Properties `Core::$data` and `Core::$globals` are now protected. + +`Fixed` +* Fix PHPUnit test: `CompilerTest::testConstants`. + +## 1.3.1 (2016-12-16) +* Now fully compatible with PHP7.0 and PHP7.1. + +`Changed` +* Rename class `Dwoo\Template\String` to `Dwoo\Template\Str`. + +`Fixed` +* Fixing all adapters. +* Fixing constant calls from classes with namespaces. + +## 1.3.0 (2016-09-25) +`Added` +* Add namespaces. +* Add new PHPDoc block **Copyright** for each files. + +`Changed` +* Follows [PHP Coding Standards Fixer](http://cs.sensiolabs.org/). +* Refactoring code, following [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) Standard. +* Follows PSR-1: Basic Coding Standard. +* Follows PSR-2: Cosing Style Guide. +* Follows PSR-4: Autoloader Standard instead of PSR-0. +* Update **README** examples with namespace. +* Move all plugins from `plugins/builtin` to `Dwoo/Plugins`. +* Processor `smarty_compat` become `PluginSmartyCompatible`. +* All plugins **functions** and **classes** names MUST start with `Plugin` keyword. +* All plugins filename MUST have the same name as the **function** or **class**. +* Plugins name changed from **underscore_case** to **CamelCase** (e.g. `Dwoo_Plugin_assign_compile` is now `PluginAssignCompile`). +* Helper `Dwoo_Plugin_array_compile` move to `Dwoo\Plugins\Helpers\PluginArrayCompile`. + +`Removed` +* Delete `Dwoo` class, now you need to use: `new Dwoo\Core()`. +* Delete method `Dwoo\Core::output()`, use now `echo Dwoo\Core::get()` method. +* Last parameter `$_output` of `Dwoo\Core::get()` method has been removed, use `echo Dwoo\Core::get()` instead. + +## 1.2.3 (2016-09-15) +`Added` +* Continuously integrated **Travis** config file. +* Add new method `Dwoo_Core::ClearCompiled()` to clear compiled templates. +* Add new PHPDoc `@author` and `@copyright` tags. + +`Removed` +* Unreachable statements. + +`Fixed` +* PHPUnit config file. +* PHPUnit tests classes. +* Fix `Dwoo_Core::clearCache()`, remove all files even `.html`. +* Fix all PHPDoc. +* Fix return statements in correlation with PHPDoc. + +`Deprecated` +* Method `Dwoo_Core::output()` is now deprecated, will be removed in **1.3.0**. + +## 1.2.2 (2016-09-10) +`Added` +* Add support for `XOR` and `^` operators. +* Add support for closure when adding a new plugin with: `$dwoo->addPlugin()`. + +`Removed` +* File `lib/Dwoo.compiled.php` has been removed. +* File `lib/dwooAutoload.php` has been removed, use **Composer** to autoload classes. + +## 1.2.1 (2014-04-20) +`Added` +* Support for Composer, adding `composer.json` file. + +`Changed` +* Follows PSR-0: Autoloader Standard. +* Minimum PHP version required is now **PHP 5.3** + +`Deprecated` +* Dwoo class is now deprecated, will be removed in **1.3.0**. + +`Fixed` +* Fix for reading `unassigned _filter` from `Zend_View_Abstract` for **ZendFramework** adapter. +* Fixed `{capture}` contents lacking the output of block plugins when multiple block plugins are used. +* Fixed Dwoo compiler to not unnecessarily fail at statements involving method calls and multiple arguments. +* Fixed use of multiple modifiers in combination with method calls. +* Fixed compiler to work with modifiers on method calls in named parameters. +* Fixed stripping of newlines at the very beginning of block tags. +* Fixed compiler to support method calls in delimited strings. +* Fix opcache and apc compatibility. + +`Removed` +* Remove include so autoloader can handle it. + +## 1.2.0 (2013-02-15) +`Deprecated` +* BC Break: Dwoo_Core::isArray had to be fixed and it has been split up + in 3 methods, isArray (for array access), isTraversable (for foreach) + and count (just a helper that counts anything). It won't affect you + unless you built some plugin depending on isArray, in which case you + should check all works fine still +* BC Break: The fix for objects implemented __get but not __isset that was + applied in 1.1.1 was causing problems for people implementing it + correctly, therefore it had to be reverted. If you are affected you must + implement __isset() properly +* BC Break: Dwoo_Core::get now returns a new Dwoo_Core instance if it's + evaluating a template already, this only affects you if you built your + own include-like plugin, see the changes to include plugin in revision + 346 [http://bugs.dwoo.org/dwoo/browse_code/revision/345] for more infos + +`Added` +* Added support for nested blocks for template inheritance, block names + must be unique, overriding of any block is done at the top level, thanks + to Ian Carpenter for the patch +* Added {return} plugin that allows any included template to return + variables into the one that included it, or to the main controller + code via $dwoo->getReturnValues() +* Added support for static vars and methods access on namespaced classes +* Moved Dwoo code to Dwoo_Core that is extended by Dwoo, so you can use + the Dwoo directory as an svn:externals without problems now and just + use Dwoo_Core in place of Dwoo in your code +* Improved parsing of array() to support real php array syntax as well + as variables as array keys, thanks to acecream for the help +* Improved parsing of named parameters that can now be quoted +* Added support for instance and static method calls white-listing in + Dwoo_Security_Policy (see allowMethod()), this is hardly efficient + though for instance calls since it has to do runtime checks so use + it with caution + +`Fixed` +* Fixed PHP parse errors being generated in compiled templates when + {dynamic} was nested +* Fixed extends bug when calling {extends} with parenthesis +* Fixed a double escaping bug when a variable was assigned to another one +* Added $this->viewParam support to ZendFramework adapter through a + Dwoo_Adapters_ZendFramework_Dwoo class that extends Dwoo, you should use + this if you called setEngine() on the ZF view +* Fixed parsing of blocks with a huge (25K+) content +* Fixed parsing of quoted keywords in if statements, like 'not' was + parsed as ! because using {if not $foo} is valid, but it was impossible + to use them even as string. +* Fixed parsing bug with method calls used as arguments with a comma + following. +* Fixed parsing of function/constants that start with an underscore, + thanks to Dominik del Bondio for the patch +* Adapters: Agavi: Added support for multiple plugin directories in the + config, thanks to Mike Seth for the patch +* Fixed endless loop when parsing an invalid else block +* Fixed custom compilable plugins not being recognized as such + +## 1.1.1 (2010-02-07) +`Added` +* Added {optional} plugin that just prints an optional var without any + notice if it doesn't exist +* Added Dwoo::setTemplate() for testing purposes mostly + +`Fixed` +* Fixed an {extends} parsing bug that prevented the use of single-quotes + around the parent template's filename +* Fixed a security issue, if you didn't use a custom compiler factory but + passed the compiler directly to the get method with autoEscape enabled, + the autoEscape was disabled in included templates - Thanks to Fabien + Potencier for notifying me. +* Fixed a bug in {safe} when using variable-variables it would sometimes + corrupt the var name resulting in blank output +* Fixed a bug when accessing array indices that contain a minus sign, it + is now possible to access those using {$var[index-foo]}, + {$var['index-foo']} or {$index="index-foo"} {$var[$index]} +* Fixed a bug in {tif} that didn't work when 0 was given as the true or + false value +* Fixed a bug when using the autoEscape feature with sub-templates (the + compiled sub-template couldn't access the dwoo charset property, + resulting in a fatal error) +* Fixed a property reading bug on objects that implemented __get but not + __isset, implementing __isset is however very much recommended +* Fixed a Dwoo_Data bug in the append method when the index didn't exist + yet it threw a notice +* Fixed a bug when accessing global vars from a sub-template +* Fixed a couple bugs in the {dynamic} plugin with regard to using plugins + within a dynamic block +* Fixed a compilation bug when using a PluginProxy with highly nested calls +* Fixed a {load_templates} bug, plugins used in external templates were not + loaded correctly, same for custom user plugins +* Cached templates now check the source template for modification before + outputting the cached version + +`Removed` +* Removed a couple of `@` operator calls to `file_get_contents` + +## 1.1.0 (2009-07-18) +`Deprecated` +* BC Break: Dwoo::initGlobals() is only called once during the Dwoo object + construction. If you had overriden it and need to update global data + before each template is executed you should instead override + Dwoo::initRuntimeVars() and push stuff in the globals array there. Also + be aware that this means captured data, foreach values and so-on will + persist from a parent template to an included one (but the include's + changes will not be reflected on the parent), and from a template + to the next if you render several in series. + +`Added` +* Added {template} plugin that allows you to define sub-templates and then + call them (works recursively too, good for menus and lists) +* Added {load_templates} to load external sub-templates into your file +* Allowed string concatenation assignments with {$foo.="bar"} +* Allowed access of static properties as {Foo::$bar} +* Plugins/Helpers that use a dynamic number of arguments through + func_get_args are now working since the compiler lets any arguments in + excess pass through +* Adapters: CodeIgniter: the adapter by Stefan Verstege has been added to + core and he will be its maintainer from now on +* Adapters: CakePHP: this adapter is now added to core and is designed to + work with CakePHP 1.2 + +`Fixed` +* Adapters: Zend: Denis Arh is now appointed maintainer of that part and + fixed a few things since 1.0.1 +* The include_path isn't altered anymore, hopefully saving some stat calls +* User classes extending Dwoo_Template_File are now supported better with + regard to includes - Thanks to the Kayako.com team for the patch +* Objects now act like arrays when you access non-existing properties on + them (i.e. it outputs a notice only if it's output straight, and none + when passed to a function) +* For can now iterate backwards if you input numbers, it won't work with + variables though +* Slight performance improvement with big inheritance trees +* No more double-slashes in template paths since this seemed to cause + slight performance issues +* Fixed a bug with parsing AND/OR keywords in conditionals when they were + followed by round brackets +* Fixed assignments not handling multi-line values correctly +* Fixed parameter parsing issue when a plugin name was all uppercased +* Fixed assignments failing with autoEscape enabled +* Fixed parsing of vars with string keys that was too greedy +* Fixed an optimization causing foreach/for/loop variables not being + accessible when the foreach/.. name was set dynamically +* Fixed parsing of comments that were on top of the file when there are + spaces at the end of it +* Dwoo_Template::$chmod is now enforced for directories as well (#18) +* Many new unit tests to improve code coverage and a bunch of bug fixes + that resulted, but I didn't really keep track of them + +## 1.0.1 (2008-12-24) +`Fixed` +* Direct assignments like {$foo = 5} now allow spaces around the operator +* Fixed a {foreach} bug with the implode argument +* Fixed modulo operator in if statements +* Fixed date_format handling of negative and small unix timestamps +* Fixed a weird reference bug with ZF and includes.. whatever but thanks + to Denis Arh for the patch + +## 1.0.0 (2008-10-23) +`Deprecated` +* BC Break: Small one that probably won't affect anyone, but it makes the + PluginProxy feature much stronger. Basically if you used a custom one you + will get a fatal error and need to update it to conform to the new + IPluginProxy interface, that's it + +`Added` +* Compiler: the modifier syntax (|foo) can now be applied on functions and on + complex variables i.e. {$obj->getStuff()|upper} or {lower('foo')|upper} +* SmartyCompat: Added a {section} plugin but I strongly discourage using it, + it was really made to support legacy templates, since {for} doesn't have to + handle {section}-BC anymore, it has been cleaned up a lot and the last + $skip parameter has been dropped + +`Fixed` +* The core Dwoo class doesn't need writable compile/cache dirs in the + constructor anymore so you can provide custom ones later through + ->setCompile(/Cache)Dir - thanks to Denis Arh for the patch +* Adapters: Zend: major overhaul thanks to Denis Arh, templates files should + probably be moved in the scripts subfolder after this update though, and + the settings array has changed a bit, you will get warnings if you don't + update the code anyway +* Plugins: improved the dump plugin, it now displays object's properties + and optionally public methods (if the new show_methods arg is set to true) + - thanks to Stephan Wentz for the patch +* Adapters: Zend: Added parameters to provide a custom engine (extends Dwoo) + or a custom data class (extends Dwoo_Data) - thanks to Maxime Merian for + the patch +* Compiler: added Dwoo_Compiler->setNestedCommentsHandling(true) to enable + parsing of nested comments (i.e. {* {* *} *} becomes a valid comment, useful + to comment out big chunks of code containing comments) +* Lines containing only comments and whitespace are now entirely removed +* Removed comments do not mess up the line count anymore (for error messages) +* Fixed parsing bug in {func()->propertyOfReturnedObject} +* Fixed file template class reading from the string compiler factory - thanks + to MrOxiMoron for the patch +* Fixed handling of variable variables that contained non standard characters +* Fixed a 1.0.0beta regression that messed with custom plugin directories + on Windows +* SmartyCompat: Fixed a few bugs in the adapter and processor - thanks to + Stefan Moonen for the patches + +## 1.0.0beta (2008-09-08) +`Deprecated` +* Important note : Dwoo.php should not be included directly anymore, please + read the UPGRADE_NOTES file for more infos on the matter, if you don't + your Dwoo install will most likely break after the update anyway +* BC Break: {include} and {extends} now support the include path properly, + which means that if you include "foo/bar.html" from _any_ template and you + have an include path set on your template object, it will look in all those + paths for foo/bar.html. If you use relative paths, for example + if you include "../foo/bar.html" AND have an include path set, you will now + have a problem, because you can't mix both approaches, otherwise you should + be fine, so to fix this you should convert your relative includes/extends + +`Added` +* Adapters: Added the Agavi interface for Dwoo + (see /Dwoo/Adapters/Agavi/README) +* API: Added Dwoo_Compilation_Exception methods getCompiler() and + getTemplate() so you can catch the exception and use those to build a nicer + error view with all the details you want +* Plugins: Added a mode parameter to {strip} to allow stripping of javascript + code blocks that use "// comments", because without this special mode the + comments result in syntax errors + +`Fixed` +* The Compiler now ensures that a template starting with get() is now stricter as to what it accepts as a "template", only + Dwoo_ITemplate objects or valid filenames are accepted +* Foreach and other similar plugins that support "else" now only count() + their input before processing when an else block follows +* Various optimizations +* Fixed compiler bug that created a parse error when you had comments in an + extended template +* Fixed extends bug when extending files in other directories using relative + paths +* Fixed parsing bug with "|modifier:param|modifier2} with:colon after it" +* Bug fixed with smarty functions called with no parameters (in compat mode) +* Fixed Dwoo::isArray() check, objects implementing ArrayAccess are now + valid (thanks to Daniel Cousineau for the patch) +* Fixed compiler warning when doing {func()->method()} or {func()->property} +* Fixed compiled/cached files being written in the wrong place when the path + to the template contains "../"s +* Fixed {if} failing with conditions using upper case operators (i.e. AND) + +## 0.9.3 (2008-08-03) +`Added` +* Adapters: Added the ZendFramework interface for Dwoo + (see /Dwoo/Adapters/ZendFramework/README) +* Plugins: Added the {a} block plugin to generate tags +* Syntax: Added the ";" token that allows to group multiple instructions in one + single template tag, example: {if $foo; "> $foo";$bar;/} is equal to: + {if $foo}> {$foo}{$bar}{/} - This also allow block operations such as: + {a http://url.com; "Text" /} which equals to {a http://url.com}Text{/} +* Syntax: Block plugins that you want to call without content can be + self-closed just like XML tags (e.g. {a "http://url.com" /} ). Be careful not + to close a non-block plugin like that however, since it will close it's + parent block +* Syntax: Static methods can be called using {Class::method()} +* Syntax: It is now possible to use a plugin's result as an object and call + a method or read a property from it, i.e. {fetchObject()->doStuff()} +* API: Added Dwoo_Plugin::paramsToAttributes() utility function to help + with the creation of compilable xml/html-related plugins +* API: Added Dwoo->setPluginProxy() and Dwoo_IPluginProxy that allow you to + hook into the compiler's plugin subsystem to provide your own plugin calls. + Thanks to Denis Arh for the patch + => http://forum.dwoo.org/viewtopic.php?id=70 +* API: Dwoo->addPlugin() has a third parameter to mark a plugin as compilable +* Compiler supports method calls into a method call's parameters + +`Fixed` +* Dwoo_Compiler::implode_r is now public/static so it can be used in other + places such as plugin proxies +* Syntax: Math expressions in strings are now only allowed when the entire + expression is delimited, e.g. {"/$foo/$bar"} evaluates as just that while + {"/`$foo/$bar`"} will result in "/".($foo/$bar), therefore processing the / + as a division, this is better since URLs using / are far more common than + math in strings + => http://forum.dwoo.org/viewtopic.php?id=50 +* Compiler now allows the use of the right delimiter inside strings (e.g. {"}"}) +* Fixed a bug preventing if blocks containing a {elseif} followed by {else} +* Fixed the Dwoo_ILoader interface and implemented it in Dwoo_Loader now + => http://forum.dwoo.org/viewtopic.php?id=70 +* Fixed a compileId auto-generation creating conflicts +* Include allows paths going in upper levels now such as : "../foo.html" +* Some compiler fixes regarding custom plugins + +## 0.9.2 (2008-06-28) +`Deprecated` +* BC Break: Renamed the {strip} modifier/function to {whitespace}, this does + not affect the strip block, that has been moved off the compiler into a + plugin. Which is why the name conflict had to be resolved. Please report + any issue you might encounter when using the strip block +* BC Break: Changed the function signature of Dwoo_Block_Plugin::postProcessing + it only affects you if you had any custom block plugins, see UPGRADE_NOTES + for more details +* BC Break: Dwoo_ITemplate::cache() must now return the cached file name or + false if caching failed, only affects you if you had a custom template class + and implemented cache() yourself +* BC Break: Dwoo_Loader is not static anymore so anything you did on it directly + will break. Use $dwoo->getLoader()->addDirectory() instead of + Dwoo_Loader::addDirectory() for example +* BC Break: DWOO_COMPILE_DIRECTORY and DWOO_CACHE_DIRECTORY are gone, set those + paths in Dwoo's constructor (i.e. new Dwoo('compiledir', 'cachedir')) if you + need to override the default ones + +`Added` +* Plugins: Added {dynamic} that allows cached templates to have dynamic + (non-cached) parts, when rendering a cached page, the dynamic parts can still + use the variables you provides +* Plugins: Added {tif} that acts as a ternary if / allows you to use a ternary + operator inside it +* API: Added a Dwoo_ILoader interface if you want to provide a custom plugin + loading solution (use $dwoo->setLoader($myLoader)) +* Added line numbers in compilation errors and improved several error messages +* Added magic object-access methods to Dwoo_Data, so you can assign values by + doing $data->var = $val; instead of $data->assign('var', $val); +* Added get()/unassign()/isAssigned() methods to read, remove and check for the + presence of a var inside a Dwoo_Data object + +`Fixed` +* Plugins: added a fifth 'string $implode' parameter to {foreach}, it prints + whatever you provide it between each item of the foreach, just like implode() +* Plugins: added a fourth 'bool $case_sensitive' parameter to {replace} +* Plugins: added a fourth 'bool $trim' parameter to {capture} that trims + the captured text +* Made the dependency on the hash extension optional +* Fixed compiler bug that prevented method calls combined with named parameters +* Fixed compiler bug that prevented the % shortcut for constants to work within + function calls (basically it only worked as {%CONST}) +* Fixed compiler bug that prevented empty() to be called +* Fixed several modifier parsing bugs + => http://forum.dwoo.org/viewtopic.php?id=27 +* Fixed empty string parsing in modifier applied to variables +* Fixed compiler handling of {template_tag} where there was + no ';' at the end of the php tag +* Allowed method calls to work with named parameters +* Removed checks for methods/properties being present on objects before calling + them since these can be handled by __get() and __call() + => http://forum.dwoo.org/viewtopic.php?id=22 +* Calling {func (params)} (with the space between function and params) is now + allowed + => http://forum.dwoo.org/viewtopic.php?id=21 +* The compiler now allows \r, \n and \t to be parameter splitters as well as + "," and " ". You can therefore split complex function calls on multiple lines +* Converted most of the code to follow PEAR Coding Standards, hopefully this + didn't break anything that the tests missed +* A few other minor or internal changes + +## 0.9.1 (2008-05-30) +`Added` ++ API: Added Dwoo_Compiler->setAutoEscape() and getAutoEscape() to modify the + automatic html entity escaping setting. This is disabled by default, and when + enabled can be overriden with the {safe $var} plugin or the + {auto_escape disable} block plugin. The block plugin can also be used to + enable this mode from within a template ++ Syntax: Mixing named and unnamed parameters is now allowed, as long as the + unnamed ones appear first ++ Syntax: Added {/} shortcut that closes the last opened block + +`Fixed` +* Optimized scope-handling functions, {loop} and {with} are now slightly faster +* Fixed a bug in {date_format} that prevented anything but unix timestamps to + work +* {literal} and {strip} now follow the LooseOpeningsHandling setting +* Fixed complex variables (i.e. {$_root[$c[$x.0]].0}) parsing bugs +* Fixed $dwoo->addResource() breaking if the resource class was not loaded yet, + autoload should now be called (thanks mike) +* Fixed a block stack bug that messed up {textformat} and possibly usermade + block plugins + +## 0.9.0 (2008-05-10) +`Deprecated` +* BC Break: changed all class names to be PEAR compliant (aka use underscores + to separate words/paths), sorry about that but I better do it now than in + six months +* BC Break: $dwoo->output() and get() have been swapped internally, but it + doesn't change anything for you unless you called output(*, *, *, true) + directly to emulate get(). This was done to reduce some overhead +* BC Break: $dwoo->getTemplate() changed to $dwoo->templateFactory() and + $dwoo->getCurrentTemplate() changed to $dwoo->getTemplate() for consistency + among all classes and factory functions + +`Added` ++ Added a compiled version of Dwoo that loads faster (especially with opcode + caches such as APC), include Dwoo.compiled.php instead of Dwoo.php on + production but if you want to file a bug use Dwoo.php please as it allows + you to get the proper file/line number where an error occurs. Do not remove + all other files however since they are not all included in the compiled + package ++ Plugins: Added {extends} and {block} to handle template inheritance, read + more about it at http://wiki.dwoo.org/index.php/TemplateInheritance ++ Plugins: Added {loop} that combines {foreach} and {with}, see + http://wiki.dwoo.org/index.php/Block:loop for details ++ Plugins: Added {do} that executes whatever you feed it whitout echoing the + result, used internally for extends but you can use it if required ++ Plugins: Added {eol} that prints an end of line character (OS-specific) ++ Syntax: Added shortcut for {$dwoo.const.*} using '%', for example you can use + {%FOO} instead of {$dwoo.const.FOO} ++ Syntax: When using named parameters, typing a parameter name without any + value is the same as typing param=true, for example {foo name="test" bar} and + {foo name="test" bar=true} are equals, can be useful for very complex plugins + with huge amounts of parameters. ++ Syntax: Added support for {$foo+=5}, {$foo="a"}, {$foo++} and {$foo--} ++ Syntax: Added shortcut for $dwoo.*, you can now use {$.foreach.foo} instead + of {$dwoo.foreach.foo} for example, applies to all $dwoo.* vars ++ Syntax: Added $ as a shortcut for current scope, $_ for $_parent and $__ for + $_root ++ API: Added getSource(), getUid() and getResourceIdentifier() to Dwoo_ITemplate ++ API: Added setSecurityPolicy() too Dwoo_ICompiler and modified the arguments + of its compile() method ++ API: Added a bunch of utility functions to Dwoo_Compiler, allowing compiled + plugins to access more of the compiler internals ++ Both cache and compile IDs can now have slashes in them to create subfolders + in the cache/compile dirs ++ Added a DWOO_CHMOD constant that, if set before you include Dwoo, allows you + to define the file mode of all the file/directories Dwoo will write, defaults + to 0777 ++ Added a 'data' argument to {include} to be able to feed data directly into it + +`Fixed` +* The compiler now throws Dwoo_Compilation_Exception exceptions upon failure + and security problems lead to a Dwoo_Security_Exception being thrown. Runtime + plugin errors and such trigger simple php errors to allow the template + execution to complete +* Fixed a potential concurrency issue (thanks to Rasmus Schultz for the patch) +* Moved all files to Dwoo/Class.php excepted for the core Dwoo.php file +* Various performance improvements, including the removal of a lot of isset() + calls. Doing {$foo} if foo is undefined will now display a PHP warning, but + doing {foreach $foo}..{/foreach} will not however, that way you don't have + to do {if isset($foo)} before the foreach, but automated isset() calls don't + impact performance as much as they did before. +* API: Dwoo_ITemplate->clearCache now requires a Dwoo instance as its first arg, + should not affect you unless you built a custom template class from scratch +* Reworked Dwoo template rendering to avoid variable conflicts with plugins +* {include} now uses the current resource if none is provided instead of using + file as it did before +* Dwoo uses include path instead of absolute includes +* Changed all line endings to Unix (line feed only) and all spaces left have + been converted to tabs (tabspace 4) +* TestFest happened early for Dwoo, lots of new tests and more code covered +* Fixed a regression in the handling of custom class plugins +* Fixed various bugs in the Adapter class and related smarty compatibility + features +* Fixed a classpath rebuilding bug that occured on some UNIX platforms due to + glob() returning false sometimes for empty folders +* Fixed a bug in Dwoo_Security_Policy->getAllowedDirectories(), no security + issue though +* Fixed a bug in Dwoo::setScope affecting {loop} and {with} +* Fixed a parsing bug when doing {"string"|modifier:$var} + +## 0.3.4 (2008-04-09) +`Deprecated` +* BC Break: DWOO_PATH constant changed to DWOO_DIRECTORY +* BC Break: Smarty's @ operator for modifiers is now reversed, for example + $array|reverse will reverse the items of that array while $array|@reverse + will reverse each item of the given array (as if you used array_map) + +`Added` ++ Syntax: Added support for method calls on objects i.e. {$foo->bar()} ++ Added support for smarty security features, see the DwooSecurityPolicy class + and $dwoo->setSecurityPolicy() ++ API: Added a DwooCompiler->setLooseOpeningHandling() method that, if set to + true, allows tags to contain spaces between the opening bracket and the + content. Turned off by default as it allows to compile files containing + css and javascript without the need to escape it through {literal} or \{ ++ Added DWOO_CACHE_DIRECTORY and DWOO_COMPILE_DIRECTORY constants that you can + set before including Dwoo.php to override the defaults (although + Dwoo->setCacheDir/setCompileDir() still work to change that if required) ++ Added the DwooException class ++ Smarty: Added partial support for register_object(), unregister_object() and + get_registered_object(). All features can not be supported by the adapter + though so you might get compatibility warnings + +`Fixed` +* Fixed {elseif} bug that appeared when multiple elseif tags were used in a row +* Syntax: Improved simple math support to work within variable variables + (i.e. you can do {$array[$index+1]}) and within strings as well. To prevent + this enclose the variables in backticks (i.e. {"$foo/$bar"} will do the math + while {"`$foo`/$bar"} won't as $foo is properly delimited) +* Changed DwooLoader::addDirectory() so that it writes the class paths cache + into DWOO_COMPILE_DIRECTORY, that way you don't have to make your plugin + directory writable +* Made all the error triggering more consistent +* Changed automatic cacheId generation in DwooTemplateFile/String to be faster + +## 0.3.3 (2008-03-19) +`Added` ++ Syntax: Added support for $dwoo.const.CONSTANT and + $dwoo.const.Class::CONSTANT to read PHP constants from the template ++ Syntax: Added support for on/off/yes/no, that work as aliases for true/false ++ Syntax: Added the $dwoo.charset global variable ++ Plugins: Added {withelse} and made {with} compatible with {else} also ++ API: Added left/right delimiters customization, see DwooCompiler->setDelimiters() ++ API: Added DwooCompiler->triggerError() ++ API: Added Dwoo->clearCache() and DwooITemplate->clearCache() methods ++ Smarty: The smartyCompat prefilter converts {section} tags into {for} tags on the + fly, however it's not guaranteed to work with *all* section tags, let me know if + it breaks for you + +`Fixed` +* {with} now skips the entire block if it's variable doesn't exist, so by + itself it acts as if you would do {if $var}{with $var}{/with}{/if} +* Each resource has a compiler factory function assigned to it, allowing you to + easily provide a custom compiler without loading it on every page +* OutputFilters are now simply called Filters (they still use DwooFilter) +* Pre/PostFilters have become Pre/PostProcessors (they now use DwooProcessor) +* Compiler: Fixed parsing bug that prevented function names of 1character +* Compiler: Changed internal handling of variables to fix some errors being + thrown with specific cases +* Reorganized Dwoo/DwooCompiler and fully commented all the core classes + and interfaces + +## 0.3.2 (2008-03-09) +`Added` ++ Added access to superglobals through $dwoo.get.value, $dwoo.post.value, + etc. ++ Added outputFilters to Dwoo (use Dwoo->addOutputFilter and + Dwoo->removeOutputFilter) ++ Added preFilters and postFilters to DwooCompiler (use + DwooCompiler->addPreFilter, etc) ++ Added a html_format output filter that intends properly the html code, + use it only on full page templates ++ Plugins: Added {for} and {forelse} which allow to loop over an array or to + loop over a range of numbers ++ Plugins: Added {mailto}, {counter}, {eval}, {fetch} and {include} ++ Syntax : Enhanced support for implicit math operations, + {$var+$var2*var3+5} now works. Operations are executed from left to right + though, there is no operator priority. (i.e. 1+1*2 = (1+1)*2 = 4, not 3) ++ API: Added resources support through DwooITemplate implementations and + Dwoo->addResource() ++ API: Added Dwoo->getTemplate() to get the currently running template object ++ API: Added DwooCompiler::getInstance() to use only one compiler object when + rendering from the default compiler and to provide you with a singleton if + it's easier, however the class is not a singleton in the sense that it can + be instantiated separately ++ API: Added a factory method on DwooITemplate to support resources creation ++ Added a release tag so that all compiled templates are forced to recompile + after an update, however it is recommended to cleanup your "compiled" + directory now and then as each release uses new filenames ++ Added an abstract DwooFilter class that you can extend to build filters + +`Fixed` +* PHP function calls are now case insensitive +* Syntax: The compiler now parses expressions before modifiers, allowing for + {$var/2|number_format} for example +* DwooTemplateFile now extends DwooTemplateString instead of the other way + around as it was before +* {else} is now a general purpose plugin that can act as 'else' for foreach, + for and if/elseif, foreachelse is still available though + +## 0.3.1 (2008-03-05) +`Added` ++ Added {cycle} function ++ Syntax : Enabled support for associative arrays using + array(key="value", key2=5) for example, which you can assign or use in a + foreach directly ++ Syntax : Added support for {$var +-/*% X} (i.e. {$var + 4}), useful for + simple math operations without the math plugin ++ API : Added append/appendByRef to DwooData ++ Completely rebuilt DwooSmartyAdapter, it should "work" and fail silently if + you use a non supported function now, however you can set + $smarty->show_compat_errors=true; on it to receive notices about unsupported + features that you use + +`Fixed` +* Bug fixed in {literal} parsing +* Bug fixed in smarty functions handling +* API : Moved Plugin types constants to Dwoo so the compiler doesn't have to + be loaded unles really required +* API : Moved globals and var reinitialization in Dwoo into their own methods + so that child classes can easily add globals +* Some improvements in the compiler output +* Some changes in the cache handling of DwooTemplateFile + +`Removed` +- Special thanks to Andrew Collins that found many of the bugs fixed in this + release + +## 0.3.0 (2008-03-02) +`Added` ++ Full template cache support ++ DwooTemplateString class to load templates from a string ++ Dwoo::VERSION constant ++ {dump} plugin to print out variables ++ Unit tests (with PHPUnit) covering 73% of the codebase right now, which + should help reducing regression bugs in the next versions. ++ Started commenting (with phpdocs) properly all the classes, should be + complete for 0.4.0 + +`Fixed` +* {capture} is now compilable and has a new boolean flag to append output into + the target variable instead of overwriting +* {foreach} supports direct input (instead of only variables), allowing + constructs like {foreach array(a,b,c) val}{$val}{/foreach} for example that + would output abc. +* pre/postProcessing functions in block plugins now receive an array of named + parameters instead of numbered +* Major refactoring of DwooTemplateFile and DwooCompiler +* Cleaned up members visibility in Dwoo/DwooCompiler +* Fixes in the compiler parsing and general variables handling +* Multiple bugfixes here and there thanks to the unit tests +* Optimized {foreach} a lot + +## 0.2.1 (2008-02-19) +`Fixed` +* Compiler fixes for argument parsing and handling of Smarty plugins + +## 0.2.0 (2008-02-14) +`Added` ++ Added support for plugins made for Smarty (that includes modifiers, + functions and blocks). Not thoroughly tested. ++ Major API changes in the way Dwoo must be run, it's now much more + flexible and should not change too much in the future. ++ Added support for custom plugins, filters should come in the next version + although the API to register them is already in. + +## 0.1.0 (2008-02-08) +* Initial release \ No newline at end of file diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/LICENSE.md b/system/templateEngines/Dwoo/dwoo/dwoo/LICENSE.md new file mode 100644 index 0000000..02bbb60 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/LICENSE.md @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/README.md b/system/templateEngines/Dwoo/dwoo/dwoo/README.md new file mode 100644 index 0000000..63786a9 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/README.md @@ -0,0 +1,95 @@ +Dwoo +==== +[![Latest Stable Version](https://poser.pugx.org/dwoo/dwoo/v/stable?format=flat-square)](https://packagist.org/packages/dwoo/dwoo) +[![Total Downloads](https://poser.pugx.org/dwoo/dwoo/downloads?format=flat-square)](https://packagist.org/packages/dwoo/dwoo) +[![License](https://poser.pugx.org/dwoo/dwoo/license?format=flat-square)](https://packagist.org/packages/dwoo/dwoo) +[![Build Status](https://travis-ci.org/dwoo-project/dwoo.svg?branch=master)](https://travis-ci.org/dwoo-project/dwoo) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/dwoo-project/dwoo/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dwoo-project/dwoo/?branch=master) +[![Gitter](https://badges.gitter.im/dwoo_project/support.svg)](https://gitter.im/dwoo_project/support?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Dwoo is a PHP5/PHP7 Template Engine that was started in early 2008. The idea came +from the fact that Smarty, a well known template engine, is getting older and +older. It carries the weight of it's age, having old features that are +inconsistent compared to newer ones, being written for PHP4 its Object +Oriented aspect doesn't take advantage of PHP5's more advanced features in +the area, etc. Hence Dwoo was born, hoping to provide a more up to date and +stronger engine. + +So far it has proven to be faster than Smarty in many areas, and it provides +a compatibility layer to allow developers that have been using Smarty for +years to switch their application over to Dwoo progressively. + +> Dwoo **1.3.x** is compatible from **PHP 5.3.x** to **PHP 7.x** + +Documentation +============= +Dwoo's website to get the latest version is at http://dwoo.org/ +The wiki/documentation pages are available at http://dwoo.org/documentation/ + +Requirements +------------ +* PHP >= **5.3** +* PHP >= **7.0** +* [Multibyte String](http://php.net/manual/en/book.mbstring.php) + +License +======= +Dwoo is released under the [GNU LESSER GENERAL PUBLIC LICENSE V3](./LICENSE.md) license. + +Quick start - Running Dwoo +========================== + +Basic Example +------------- +```php +get() +$data = new Dwoo\Data(); +// Fill it with some data +$data->assign('foo', 'BAR'); +$data->assign('bar', 'BAZ'); + +// Outputs the result ... +echo $dwoo->get($tpl, $data); +// ... or get it to use it somewhere else +$dwoo->get($tpl, $data); +``` + +Loop Example +------------ +```php +assign('title', $article['title']); + $data->assign('content', $article['content']); + $pageContent .= $dwoo->get($tpl, $data); + + // Or use the article directly (which is a lot easier in this case) + $pageContent .= $dwoo->get($tpl, $article); +} +``` \ No newline at end of file diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/composer.json b/system/templateEngines/Dwoo/dwoo/dwoo/composer.json new file mode 100644 index 0000000..466ad41 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/composer.json @@ -0,0 +1,28 @@ +{ + "name": "dwoo/dwoo", + "license": "LGPL-3.0-only", + "type": "library", + "description": "Dwoo is a PHP5/PHP7 template engine which is (almost) fully compatible with Smarty templates and plugins, but is written from scratch for PHP5, and adds many features.", + "authors": [ + { + "name": "David Sanchez", + "email": "david38sanchez@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Dwoo\\": "lib/Dwoo/" + } + }, + "autoload-dev": { + "psr-4": { + "Dwoo\\Tests\\": "tests/" + } + }, + "require": { + "php": "^5.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "4.8.27" + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/docker-compose.yml b/system/templateEngines/Dwoo/dwoo/dwoo/docker-compose.yml new file mode 100644 index 0000000..3800bd6 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/docker-compose.yml @@ -0,0 +1,7 @@ +version: '2' +services: + phpfarm: + container_name: dwoo + image: splitbrain/phpfarm:jessie + volumes: + - ./:/usr/src/dwoo diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Block/Plugin.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Block/Plugin.php new file mode 100644 index 0000000..b05447c --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Block/Plugin.php @@ -0,0 +1,119 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Block; + +use Dwoo\Plugin as DwooPlugin; +use Dwoo\Compiler as DwooCompiler; + +/** + * Base class for block plugins. + * you have to implement the init() method, it will receive the parameters that + * are in the template code and is called when the block starts + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +abstract class Plugin extends DwooPlugin +{ + /** + * Stores the contents of the block while it runs. + * + * @var string + */ + protected $buffer = ''; + + /** + * Buffers input, override only if necessary. + * + * @var string the content that must be buffered + */ + public function buffer($input) + { + $this->buffer .= $input; + } + + // initialization code, receives the parameters from {block param1 param2} + // public function init($arg, $arg, ...); + + /** + * Called when the block ends, this is most of the time followed right away by a call + * of process() but not always, so this should be used to do any shutdown operations on the + * block object, if required. + */ + public function end() + { + } + + /** + * Called when the block output is required by a parent block. + * this must read $this->buffer and return it processed + * + * @return string + */ + public function process() + { + return $this->buffer; + } + + /** + * Called at compile time to define what the block should output in the compiled template code, happens when the + * block is declared basically this will replace the {block arg arg arg} tag in the template. + * + * @param DwooCompiler $compiler the compiler instance that calls this function + * @param array $params an array containing original and compiled parameters + * @param string $prepend that is just meant to allow a child class to call + * parent::postProcessing($compiler, $params, "foo();") to add a command before the + * default commands are executed parent::postProcessing($compiler, $params, "foo();") + * to add a command before the default commands are executed + * @param string $append that is just meant to allow a child class to call + * parent::postProcessing($compiler, $params, null, "foo();") to add a command after + * the default commands are executed parent::postProcessing($compiler, $params, null, + * "foo();") to add a command after the default commands are executed + * @param string $type the type is the plugin class name used + * + * @return string + */ + public static function preProcessing(DwooCompiler $compiler, array $params, $prepend, $append, $type) + { + return DwooCompiler::PHP_OPEN . $prepend . '$this->addStack("' . $type . '", array(' . DwooCompiler::implode_r($compiler->getCompiledParams($params)) . '));' . $append . DwooCompiler::PHP_CLOSE; + } + + /** + * Called at compile time to define what the block should output in the compiled template code, happens when the + * block is ended basically this will replace the {/block} tag in the template. + * + * @see preProcessing + * + * @param DwooCompiler $compiler the compiler instance that calls this function + * @param array $params an array containing original and compiled parameters, see preProcessing() for more + * details more details + * @param string $prepend that is just meant to allow a child class to call + * parent::postProcessing($compiler, $params, "foo();") to add a command before the + * default commands are executed parent::postProcessing($compiler, $params, "foo();") + * to add a command before the default commands are executed + * @param string $append that is just meant to allow a child class to call + * parent::postProcessing($compiler, $params, null, "foo();") to add a command after + * the default commands are executed parent::postProcessing($compiler, $params, null, + * "foo();") to add a command after the default commands are executed + * @param string $content the entire content of the block being closed + * + * @return string + */ + public static function postProcessing(DwooCompiler $compiler, array $params, $prepend, $append, $content) + { + return $content . DwooCompiler::PHP_OPEN . $prepend . '$this->delStack();' . $append . DwooCompiler::PHP_CLOSE; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Compilation/Exception.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Compilation/Exception.php new file mode 100644 index 0000000..732a8d4 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Compilation/Exception.php @@ -0,0 +1,60 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE LGPLv3 + * @version 1.3.6 + * @date 2017-03-21 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Compilation; + +use Dwoo\Exception as DwooException; +use Dwoo\Compiler as DwooCompiler; + +/** + * Dwoo compilation exception class. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class Exception extends DwooException +{ + protected $compiler; + protected $template; + + /** + * Exception constructor. + * + * @param DwooCompiler $compiler + * @param int $message + */ + public function __construct(DwooCompiler $compiler, $message) + { + $this->compiler = $compiler; + $this->template = $compiler->getCore()->getTemplate(); + parent::__construct('Compilation error at line ' . $compiler->getLine() . ' in "' . $this->template->getResourceName() . ':' . $this->template->getResourceIdentifier() . '" : ' . $message); + } + + /** + * @return DwooCompiler + */ + public function getCompiler() + { + return $this->compiler; + } + + /** + * @return \Dwoo\ITemplate|null + */ + public function getTemplate() + { + return $this->template; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Compiler.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Compiler.php new file mode 100644 index 0000000..9595bb7 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Compiler.php @@ -0,0 +1,3681 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE LGPLv3 + * @version 1.3.6 + * @date 2017-03-22 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +use Closure; +use Dwoo\Plugins\Blocks\PluginIf; +use Dwoo\Security\Exception as SecurityException; +use Dwoo\Security\Policy as SecurityPolicy; +use Dwoo\Compilation\Exception as CompilationException; +use ReflectionFunction; +use ReflectionMethod; + +/** + * Default dwoo compiler class, compiles dwoo templates into php. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class Compiler implements ICompiler +{ + /** + * Constant that represents a php opening tag. + * use it in case it needs to be adjusted + * + * @var string + */ + const PHP_OPEN = ''; + + /** + * Boolean flag to enable or disable debugging output. + * + * @var bool + */ + public $debug = false; + + /** + * Left script delimiter. + * + * @var string + */ + protected $ld = '{'; + + /** + * Left script delimiter with escaped regex meta characters. + * + * @var string + */ + protected $ldr = '\\{'; + + /** + * Right script delimiter. + * + * @var string + */ + protected $rd = '}'; + + /** + * Right script delimiter with escaped regex meta characters. + * + * @var string + */ + protected $rdr = '\\}'; + + /** + * Defines whether the nested comments should be parsed as nested or not. + * defaults to false (classic block comment parsing as in all languages) + * + * @var bool + */ + protected $allowNestedComments = false; + + /** + * Defines whether opening and closing tags can contain spaces before valid data or not. + * turn to true if you want to be sloppy with the syntax, but when set to false it allows + * to skip javascript and css tags as long as they are in the form "{ something", which is + * nice. default is false. + * + * @var bool + */ + protected $allowLooseOpenings = false; + + /** + * Defines whether the compiler will automatically html-escape variables or not. + * default is false + * + * @var bool + */ + protected $autoEscape = false; + + /** + * Security policy object. + * + * @var SecurityPolicy + */ + protected $securityPolicy; + + /** + * Stores the custom plugins registered with this compiler. + * + * @var array + */ + protected $customPlugins = array(); + + /** + * Stores the template plugins registered with this compiler. + * + * @var array + */ + protected $templatePlugins = array(); + + /** + * Stores the pre- and post-processors callbacks. + * + * @var array + */ + protected $processors = array('pre' => array(), 'post' => array()); + + /** + * Stores a list of plugins that are used in the currently compiled + * template, and that are not compilable. these plugins will be loaded + * during the template's runtime if required. + * it is a 1D array formatted as key:pluginName value:pluginType + * + * @var array + */ + protected $usedPlugins; + + /** + * Stores the template undergoing compilation. + * + * @var string + */ + protected $template; + + /** + * Stores the current pointer position inside the template. + * + * @var int + */ + protected $pointer; + + /** + * Stores the current line count inside the template for debugging purposes. + * + * @var int + */ + protected $line; + + /** + * Stores the current template source while compiling it. + * + * @var string + */ + protected $templateSource; + + /** + * Stores the data within which the scope moves. + * + * @var array + */ + protected $data; + + /** + * Variable scope of the compiler, set to null if + * it can not be resolved to a static string (i.e. if some + * plugin defines a new scope based on a variable array key). + * + * @var mixed + */ + protected $scope; + + /** + * Variable scope tree, that allows to rebuild the current + * scope if required, i.e. when going to a parent level. + * + * @var array + */ + protected $scopeTree; + + /** + * Block plugins stack, accessible through some methods. + * + * @see findBlock + * @see getCurrentBlock + * @see addBlock + * @see addCustomBlock + * @see injectBlock + * @see removeBlock + * @see removeTopBlock + * @var array + */ + protected $stack = array(); + + /** + * Current block at the top of the block plugins stack, + * accessible through getCurrentBlock. + * + * @see getCurrentBlock + * @var array + */ + protected $curBlock; + + /** + * Current dwoo object that uses this compiler, or null. + * + * @var Core + */ + public $core; + + /** + * Holds an instance of this class, used by getInstance when you don't + * provide a custom compiler in order to save resources. + * + * @var Compiler + */ + protected static $instance; + + /** + * Token types. + * + * @var int + */ + const T_UNQUOTED_STRING = 1; + const T_NUMERIC = 2; + const T_NULL = 4; + const T_BOOL = 8; + const T_MATH = 16; + const T_BREAKCHAR = 32; + + /** + * Compiler constructor. + * saves the created instance so that child templates get the same one + */ + public function __construct() + { + self::$instance = $this; + } + + /** + * Sets the delimiters to use in the templates. + * delimiters can be multi-character strings but should not be one of those as they will + * make it very hard to work with templates or might even break the compiler entirely : "\", "$", "|", ":" and + * finally "#" only if you intend to use config-vars with the #var# syntax. + * + * @param string $left left delimiter + * @param string $right right delimiter + */ + public function setDelimiters($left, $right) + { + $this->ld = $left; + $this->rd = $right; + $this->ldr = preg_quote($left, '/'); + $this->rdr = preg_quote($right, '/'); + } + + /** + * Returns the left and right template delimiters. + * + * @return array containing the left and the right delimiters + */ + public function getDelimiters() + { + return array($this->ld, $this->rd); + } + + /** + * Sets the way to handle nested comments, if set to true + * {* foo {* some other *} comment *} will be stripped correctly. + * if false it will remove {* foo {* some other *} and leave "comment *}" alone, + * this is the default behavior + * + * @param bool $allow allow nested comments or not, defaults to true (but the default internal value is false) + */ + public function setNestedCommentsHandling($allow = true) + { + $this->allowNestedComments = (bool)$allow; + } + + /** + * Returns the nested comments handling setting. + * + * @see setNestedCommentsHandling + * @return bool true if nested comments are allowed + */ + public function getNestedCommentsHandling() + { + return $this->allowNestedComments; + } + + /** + * Sets the tag openings handling strictness, if set to true, template tags can + * contain spaces before the first function/string/variable such as { $foo} is valid. + * if set to false (default setting), { $foo} is invalid but that is however a good thing + * as it allows css (i.e. #foo { color:red; }) to be parsed silently without triggering + * an error, same goes for javascript. + * + * @param bool $allow true to allow loose handling, false to restore default setting + */ + public function setLooseOpeningHandling($allow = false) + { + $this->allowLooseOpenings = (bool)$allow; + } + + /** + * Returns the tag openings handling strictness setting. + * + * @see setLooseOpeningHandling + * @return bool true if loose tags are allowed + */ + public function getLooseOpeningHandling() + { + return $this->allowLooseOpenings; + } + + /** + * Changes the auto escape setting. + * if enabled, the compiler will automatically html-escape variables, + * unless they are passed through the safe function such as {$var|safe} + * or {safe $var} + * default setting is disabled/false + * + * @param bool $enabled set to true to enable, false to disable + */ + public function setAutoEscape($enabled) + { + $this->autoEscape = (bool)$enabled; + } + + /** + * Returns the auto escape setting. + * default setting is disabled/false + * + * @return bool + */ + public function getAutoEscape() + { + return $this->autoEscape; + } + + /** + * Adds a preprocessor to the compiler, it will be called + * before the template is compiled. + * + * @param mixed $callback either a valid callback to the preprocessor or a simple name if the autoload is set to + * true + * @param bool $autoload if set to true, the preprocessor is auto-loaded from one of the plugin directories, else + * you must provide a valid callback + */ + public function addPreProcessor($callback, $autoload = false) + { + if ($autoload) { + $name = str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', Core::toCamelCase($callback)); + $class = Core::NAMESPACE_PLUGINS_PROCESSORS . $name; + + if (class_exists($class)) { + $callback = array(new $class($this), 'process'); + } elseif (function_exists($class)) { + $callback = $class; + } else { + $callback = array('autoload' => true, 'class' => $class, 'name' => $name); + } + + $this->processors['pre'][] = $callback; + } else { + $this->processors['pre'][] = $callback; + } + } + + /** + * Removes a preprocessor from the compiler. + * + * @param mixed $callback either a valid callback to the preprocessor or a simple name if it was autoloaded + */ + public function removePreProcessor($callback) + { + if (($index = array_search($callback, $this->processors['pre'], true)) !== false) { + unset($this->processors['pre'][$index]); + } elseif (($index = array_search(Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', + $callback), + $this->processors['pre'], true)) !== false) { + unset($this->processors['pre'][$index]); + } else { + $class = Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback); + foreach ($this->processors['pre'] as $index => $proc) { + if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) { + unset($this->processors['pre'][$index]); + break; + } + } + } + } + + /** + * Adds a postprocessor to the compiler, it will be called + * before the template is compiled. + * + * @param mixed $callback either a valid callback to the postprocessor or a simple name if the autoload is set to + * true + * @param bool $autoload if set to true, the postprocessor is auto-loaded from one of the plugin directories, else + * you must provide a valid callback + */ + public function addPostProcessor($callback, $autoload = false) + { + if ($autoload) { + $name = str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback); + $class = Core::NAMESPACE_PLUGINS_PROCESSORS . Core::toCamelCase($name); + + if (class_exists($class)) { + $callback = array(new $class($this), 'process'); + } elseif (function_exists($class)) { + $callback = $class; + } else { + $callback = array('autoload' => true, 'class' => $class, 'name' => $name); + } + + $this->processors['post'][] = $callback; + } else { + $this->processors['post'][] = $callback; + } + } + + /** + * Removes a postprocessor from the compiler. + * + * @param mixed $callback either a valid callback to the postprocessor or a simple name if it was autoloaded + */ + public function removePostProcessor($callback) + { + if (($index = array_search($callback, $this->processors['post'], true)) !== false) { + unset($this->processors['post'][$index]); + } elseif (($index = array_search(Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', + $callback), + $this->processors['post'], true)) !== false) { + unset($this->processors['post'][$index]); + } else { + $class = Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback); + foreach ($this->processors['post'] as $index => $proc) { + if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) { + unset($this->processors['post'][$index]); + break; + } + } + } + } + + /** + * Internal function to autoload processors at runtime if required. + * + * @param string $class the class/function name + * @param string $name the plugin name (without Dwoo_Plugin_ prefix) + * + * @return array|string + * @throws Exception + */ + protected function loadProcessor($class, $name) + { + if (!class_exists($class) && !function_exists($class)) { + try { + $this->getCore()->getLoader()->loadPlugin($name); + } + catch (Exception $e) { + throw new Exception('Processor ' . $name . ' could not be found in your plugin directories, please ensure it is in a file named ' . $name . '.php in the plugin directory'); + } + } + + if (class_exists($class)) { + return array(new $class($this), 'process'); + } + + if (function_exists($class)) { + return $class; + } + + throw new Exception('Wrong processor name, when using autoload the processor must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Processor_name"'); + } + + /** + * Adds an used plugin, this is reserved for use by the {template} plugin. + * this is required so that plugin loading bubbles up from loaded + * template files to the current one + * + * @private + * + * @param string $name function name + * @param int $type plugin type (Core::*_PLUGIN) + */ + public function addUsedPlugin($name, $type) + { + $this->usedPlugins[$name] = $type; + } + + /** + * Returns all the plugins this template uses. + * + * @private + * @return array the list of used plugins in the parsed template + */ + public function getUsedPlugins() + { + return $this->usedPlugins; + } + + /** + * Adds a template plugin, this is reserved for use by the {template} plugin. + * this is required because the template functions are not declared yet + * during compilation, so we must have a way of validating their argument + * signature without using the reflection api + * + * @private + * + * @param string $name function name + * @param array $params parameter array to help validate the function call + * @param string $uuid unique id of the function + * @param string $body function php code + */ + public function addTemplatePlugin($name, array $params, $uuid, $body = null) + { + $this->templatePlugins[$name] = array('params' => $params, 'body' => $body, 'uuid' => $uuid); + } + + /** + * Returns all the parsed sub-templates. + * + * @private + * @return array the parsed sub-templates + */ + public function getTemplatePlugins() + { + return $this->templatePlugins; + } + + /** + * Marks a template plugin as being called, which means its source must be included in the compiled template. + * + * @param string $name function name + */ + public function useTemplatePlugin($name) + { + $this->templatePlugins[$name]['called'] = true; + } + + /** + * Adds the custom plugins loaded into Dwoo to the compiler so it can load them. + * + * @see Core::addPlugin + * + * @param array $customPlugins an array of custom plugins + */ + public function setCustomPlugins(array $customPlugins) + { + $this->customPlugins = $customPlugins; + } + + /** + * Sets the security policy object to enforce some php security settings. + * use this if untrusted persons can modify templates, + * set it on the Dwoo object as it will be passed onto the compiler automatically + * + * @param SecurityPolicy $policy the security policy object + */ + public function setSecurityPolicy(SecurityPolicy $policy = null) + { + $this->securityPolicy = $policy; + } + + /** + * Returns the current security policy object or null by default. + * + * @return SecurityPolicy|null the security policy object if any + */ + public function getSecurityPolicy() + { + return $this->securityPolicy; + } + + /** + * Sets the pointer position. + * + * @param int $position the new pointer position + * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position + */ + public function setPointer($position, $isOffset = false) + { + if ($isOffset) { + $this->pointer += $position; + } else { + $this->pointer = $position; + } + } + + /** + * Returns the current pointer position, only available during compilation of a template. + * + * @return int + */ + public function getPointer() + { + return $this->pointer; + } + + /** + * Sets the line number. + * + * @param int $number the new line number + * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position + */ + public function setLine($number, $isOffset = false) + { + if ($isOffset) { + $this->line += $number; + } else { + $this->line = $number; + } + } + + /** + * Returns the current line number, only available during compilation of a template. + * + * @return int + */ + public function getLine() + { + return $this->line; + } + + /** + * Returns the dwoo object that initiated this template compilation, only available during compilation of a + * template. + * + * @return Core + */ + public function getCore() + { + return $this->core; + } + + /** + * Overwrites the template that is being compiled. + * + * @param string $newSource the template source that must replace the current one + * @param bool $fromPointer if set to true, only the source from the current pointer position is replaced + * + * @return void + */ + public function setTemplateSource($newSource, $fromPointer = false) + { + if ($fromPointer === true) { + $this->templateSource = substr($this->templateSource, 0, $this->pointer) . $newSource; + } else { + $this->templateSource = $newSource; + } + } + + /** + * Returns the template that is being compiled. + * + * @param mixed $fromPointer if set to true, only the source from the current pointer + * position is returned, if a number is given it overrides the current pointer + * + * @return string the template or partial template + */ + public function getTemplateSource($fromPointer = false) + { + if ($fromPointer === true) { + return substr($this->templateSource, $this->pointer); + } elseif (is_numeric($fromPointer)) { + return substr($this->templateSource, $fromPointer); + } else { + return $this->templateSource; + } + } + + /** + * Resets the compilation pointer, effectively restarting the compilation process. + * this is useful if a plugin modifies the template source since it might need to be recompiled + */ + public function recompile() + { + $this->setPointer(0); + } + + /** + * Compiles the provided string down to php code. + * + * @param Core $core + * @param ITemplate $template the template to compile + * + * @return string a compiled php string + * @throws CompilationException + */ + public function compile(Core $core, ITemplate $template) + { + // init vars + // $compiled = ''; + $tpl = $template->getSource(); + $ptr = 0; + $this->core = $core; + $this->template = $template; + $this->templateSource = &$tpl; + $this->pointer = &$ptr; + + while (true) { + // if pointer is at the beginning, reset everything, that allows a plugin to externally reset the compiler if everything must be reparsed + if ($ptr === 0) { + // resets variables + $this->usedPlugins = array(); + $this->data = array(); + $this->scope = &$this->data; + $this->scopeTree = array(); + $this->stack = array(); + $this->line = 1; + $this->templatePlugins = array(); + // add top level block + $compiled = $this->addBlock('TopLevelBlock', array(), 0); + $this->stack[0]['buffer'] = ''; + + if ($this->debug) { + echo "\n"; + echo 'COMPILER INIT' . "\n"; + } + + if ($this->debug) { + echo 'PROCESSING PREPROCESSORS (' . count($this->processors['pre']) . ')' . "\n"; + } + + // runs preprocessors + foreach ($this->processors['pre'] as $preProc) { + if (is_array($preProc) && isset($preProc['autoload'])) { + $preProc = $this->loadProcessor($preProc['class'], $preProc['name']); + } + if (is_array($preProc) && $preProc[0] instanceof Processor) { + $tpl = call_user_func($preProc, $tpl); + } else { + $tpl = call_user_func($preProc, $this, $tpl); + } + } + unset($preProc); + + // show template source if debug + if ($this->debug) { + echo '
    '.print_r(htmlentities($tpl), true).'
    '."\n"; + } + + // strips php tags if required by the security policy + if ($this->securityPolicy !== null) { + $search = array('{<\?php.*?\?>}'); + if (ini_get('short_open_tags')) { + $search = array('{<\?.*?\?>}', '{<%.*?%>}'); + } + switch ($this->securityPolicy->getPhpHandling()) { + case SecurityPolicy::PHP_ALLOW: + break; + case SecurityPolicy::PHP_ENCODE: + $tpl = preg_replace_callback($search, array($this, 'phpTagEncodingHelper'), $tpl); + break; + case SecurityPolicy::PHP_REMOVE: + $tpl = preg_replace($search, '', $tpl); + } + } + } + + $pos = strpos($tpl, $this->ld, $ptr); + + if ($pos === false) { + $this->push(substr($tpl, $ptr), 0); + break; + } elseif (substr($tpl, $pos - 1, 1) === '\\' && substr($tpl, $pos - 2, 1) !== '\\') { + $this->push(substr($tpl, $ptr, $pos - $ptr - 1) . $this->ld); + $ptr = $pos + strlen($this->ld); + } elseif (preg_match('/^' . $this->ldr . ($this->allowLooseOpenings ? '\s*' : '') . 'literal' . ($this->allowLooseOpenings ? '\s*' : '') . $this->rdr . '/s', substr($tpl, $pos), $litOpen)) { + if (!preg_match('/' . $this->ldr . ($this->allowLooseOpenings ? '\s*' : '') . '\/literal' . ($this->allowLooseOpenings ? '\s*' : '') . $this->rdr . '/s', $tpl, $litClose, PREG_OFFSET_CAPTURE, $pos)) { + throw new CompilationException($this, 'The {literal} blocks must be closed explicitly with {/literal}'); + } + $endpos = $litClose[0][1]; + $this->push(substr($tpl, $ptr, $pos - $ptr) . substr($tpl, $pos + strlen($litOpen[0]), $endpos - $pos - strlen($litOpen[0]))); + $ptr = $endpos + strlen($litClose[0][0]); + } else { + if (substr($tpl, $pos - 2, 1) === '\\' && substr($tpl, $pos - 1, 1) === '\\') { + $this->push(substr($tpl, $ptr, $pos - $ptr - 1)); + $ptr = $pos; + } + + $this->push(substr($tpl, $ptr, $pos - $ptr)); + $ptr = $pos; + + $pos += strlen($this->ld); + if ($this->allowLooseOpenings) { + while (substr($tpl, $pos, 1) === ' ') { + $pos += 1; + } + } else { + if (substr($tpl, $pos, 1) === ' ' || substr($tpl, $pos, 1) === "\r" || substr($tpl, $pos, 1) === "\n" || substr($tpl, $pos, 1) === "\t") { + $ptr = $pos; + $this->push($this->ld); + continue; + } + } + + // check that there is an end tag present + if (strpos($tpl, $this->rd, $pos) === false) { + throw new CompilationException($this, 'A template tag was not closed, started with "' . substr($tpl, $ptr, 30) . '"'); + } + + $ptr += strlen($this->ld); + $subptr = $ptr; + + while (true) { + $parsed = $this->parse($tpl, $subptr, null, false, 'root', $subptr); + + // reload loop if the compiler was reset + if ($ptr === 0) { + continue 2; + } + + $len = $subptr - $ptr; + $this->push($parsed, substr_count(substr($tpl, $ptr, $len), "\n")); + $ptr += $len; + + if ($parsed === false) { + break; + } + } + } + } + + $compiled .= $this->removeBlock('TopLevelBlock'); + + if ($this->debug) { + echo 'PROCESSING POSTPROCESSORS' . "\n"; + } + + foreach ($this->processors['post'] as $postProc) { + if (is_array($postProc) && isset($postProc['autoload'])) { + $postProc = $this->loadProcessor($postProc['class'], $postProc['name']); + } + if (is_array($postProc) && $postProc[0] instanceof Processor) { + $compiled = call_user_func($postProc, $compiled); + } else { + $compiled = call_user_func($postProc, $this, $compiled); + } + } + unset($postProc); + + if ($this->debug) { + echo 'COMPILATION COMPLETE : MEM USAGE : ' . memory_get_usage() . "\n"; + } + + $output = "getUsedPlugins() as $plugin => $type) { + if ($type & Core::CUSTOM_PLUGIN) { + continue; + } + + switch ($type) { + case Core::CLASS_PLUGIN: + case Core::CLASS_PLUGIN + Core::BLOCK_PLUGIN: + if (class_exists('Plugin' . $plugin) !== false) { + $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)". + "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n"; + } else { + $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_BLOCKS . "Plugin" . $plugin . "')===false)". + "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n"; + } + break; + case Core::CLASS_PLUGIN + Core::FUNC_PLUGIN: + if (class_exists('Plugin' . $plugin) !== false) { + $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)". + "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n"; + } else { + $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)". + "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n"; + } + break; + case Core::FUNC_PLUGIN: + if (function_exists('Plugin' . $plugin) !== false) { + $output .= "if (function_exists('" . "Plugin" . $plugin . "')===false)". + "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n"; + } else { + $output .= "if (function_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)". + "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n"; + } + break; + case Core::SMARTY_MODIFIER: + $output .= "if (function_exists('smarty_modifier_$plugin')===false)". + "\n\t\$this->getLoader()->loadPlugin('$plugin');\n"; + break; + case Core::SMARTY_FUNCTION: + $output .= "if (function_exists('smarty_function_$plugin')===false)". + "\n\t\$this->getLoader()->loadPlugin('$plugin');\n"; + break; + case Core::SMARTY_BLOCK: + $output .= "if (function_exists('smarty_block_$plugin')===false)". + "\n\t\$this->getLoader()->loadPlugin('$plugin');\n"; + break; + case Core::PROXY_PLUGIN: + $output .= $this->getCore()->getPluginProxy()->getLoader($plugin); + break; + default: + throw new CompilationException($this, 'Type error for ' . $plugin . ' with type' . $type); + } + } + + foreach ($this->templatePlugins as $function => $attr) { + if (isset($attr['called']) && $attr['called'] === true && !isset($attr['checked'])) { + $this->resolveSubTemplateDependencies($function); + } + } + foreach ($this->templatePlugins as $function) { + if (isset($function['called']) && $function['called'] === true) { + $output .= $function['body'] . PHP_EOL; + } + } + + $output .= $compiled . "\n?>"; + + $output = preg_replace('/(?\s*)<\?xml#is', '$1', $output); + + // add another line break after PHP closing tags that have a line break following, + // as we do not know whether it's intended, and PHP will strip it otherwise + $output = preg_replace('/(?\n/', '$0' . "\n", $output); + + if ($this->debug) { + echo '=============================================================================================' . "\n"; + $lines = preg_split('{\r\n|\n|
    }', $output); + array_shift($lines); + foreach ($lines as $i => $line) { + echo ($i + 1) . '. ' . $line . "\r\n"; + } + echo '=============================================================================================' . "\n"; + } + + $this->template = $this->dwoo = null; + $tpl = null; + + return $output; + } + + /** + * Checks what sub-templates are used in every sub-template so that we're sure they are all compiled. + * + * @param string $function the sub-template name + */ + protected function resolveSubTemplateDependencies($function) + { + if ($this->debug) { + echo 'Compiler::' . __FUNCTION__ . "\n"; + } + + $body = $this->templatePlugins[$function]['body']; + foreach ($this->templatePlugins as $func => $attr) { + if ($func !== $function && !isset($attr['called']) && strpos($body, Core::NAMESPACE_PLUGINS_FUNCTIONS . + 'Plugin' . Core::toCamelCase($func)) !== false) { + $this->templatePlugins[$func]['called'] = true; + $this->resolveSubTemplateDependencies($func); + } + } + $this->templatePlugins[$function]['checked'] = true; + } + + /** + * Adds compiled content to the current block. + * + * @param string $content the content to push + * @param int $lineCount newlines count in content, optional + * + * @throws CompilationException + */ + public function push($content, $lineCount = null) + { + if ($lineCount === null) { + $lineCount = substr_count($content, "\n"); + } + + if ($this->curBlock['buffer'] === null && count($this->stack) > 1) { + // buffer is not initialized yet (the block has just been created) + $this->stack[count($this->stack) - 2]['buffer'] .= (string)$content; + $this->curBlock['buffer'] = ''; + } else { + if (!isset($this->curBlock['buffer'])) { + throw new CompilationException($this, 'The template has been closed too early, you probably have an extra block-closing tag somewhere'); + } + // append current content to current block's buffer + $this->curBlock['buffer'] .= (string)$content; + } + $this->line += $lineCount; + } + + /** + * Sets the scope. + * set to null if the scope becomes "unstable" (i.e. too variable or unknown) so that + * variables are compiled in a more evaluative way than just $this->scope['key'] + * + * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2") + * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope + * + * @return array the current scope tree + */ + public function setScope($scope, $absolute = false) + { + $old = $this->scopeTree; + + if ($scope === null) { + unset($this->scope); + $this->scope = null; + } + + if (is_array($scope) === false) { + $scope = explode('.', $scope); + } + + if ($absolute === true) { + $this->scope = &$this->data; + $this->scopeTree = array(); + } + + while (($bit = array_shift($scope)) !== null) { + if ($bit === '_parent' || $bit === '_') { + array_pop($this->scopeTree); + reset($this->scopeTree); + $this->scope = &$this->data; + $cnt = count($this->scopeTree); + for ($i = 0; $i < $cnt; ++ $i) { + $this->scope = &$this->scope[$this->scopeTree[$i]]; + } + } elseif ($bit === '_root' || $bit === '__') { + $this->scope = &$this->data; + $this->scopeTree = array(); + } elseif (isset($this->scope[$bit])) { + $this->scope = &$this->scope[$bit]; + $this->scopeTree[] = $bit; + } else { + $this->scope[$bit] = array(); + $this->scope = &$this->scope[$bit]; + $this->scopeTree[] = $bit; + } + } + + return $old; + } + + /** + * Adds a block to the top of the block stack. + * + * @param string $type block type (name) + * @param array $params the parameters array + * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2 + * + * @return string the preProcessing() method's output + */ + public function addBlock($type, array $params, $paramtype) + { + if ($this->debug) { + echo 'Compiler::' . __FUNCTION__ . "\n"; + } + + $class = current(array_filter([ + 'Plugin' . Core::toCamelCase($type), + Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type) + ], 'class_exists')); + if (false === $class) { + $this->getCore()->getLoader()->loadPlugin($type); + } + $params = $this->mapParams($params, array($class, 'init'), $paramtype); + + $this->stack[] = array( + 'type' => $type, + 'params' => $params, + 'custom' => false, + 'class' => $class, + 'buffer' => null + ); + $this->curBlock = &$this->stack[count($this->stack) - 1]; + + return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type); + } + + /** + * Adds a custom block to the top of the block stack. + * + * @param string $type block type (name) + * @param array $params the parameters array + * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2 + * + * @return string the preProcessing() method's output + */ + public function addCustomBlock($type, array $params, $paramtype) + { + $callback = $this->customPlugins[$type]['callback']; + if (is_array($callback)) { + $class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0]; + } else { + $class = $callback; + } + + $params = $this->mapParams($params, array($class, 'init'), $paramtype); + + $this->stack[] = array( + 'type' => $type, + 'params' => $params, + 'custom' => true, + 'class' => $class, + 'buffer' => null + ); + $this->curBlock = &$this->stack[count($this->stack) - 1]; + + return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type); + } + + /** + * Injects a block at the top of the plugin stack without calling its preProcessing method. + * used by {else} blocks to re-add themselves after having closed everything up to their parent + * + * @param string $type block type (name) + * @param array $params parameters array + */ + public function injectBlock($type, array $params) + { + if ($this->debug) { + echo 'Compiler::' . __FUNCTION__ . "\n"; + } + + $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type); + if (class_exists($class) === false) { + $this->getCore()->getLoader()->loadPlugin($type); + } + $this->stack[] = array( + 'type' => $type, + 'params' => $params, + 'custom' => false, + 'class' => $class, + 'buffer' => null + ); + $this->curBlock = &$this->stack[count($this->stack) - 1]; + } + + /** + * Removes the closest-to-top block of the given type and all other + * blocks encountered while going down the block stack. + * + * @param string $type block type (name) + * + * @return string the output of all postProcessing() method's return values of the closed blocks + * @throws CompilationException + */ + public function removeBlock($type) + { + if ($this->debug) { + echo 'Compiler::' . __FUNCTION__ . "\n"; + } + + $output = ''; + + $pluginType = $this->getPluginType($type); + if ($pluginType & Core::SMARTY_BLOCK) { + $type = 'Smartyinterface'; + } + while (true) { + while ($top = array_pop($this->stack)) { + if ($top['custom']) { + $class = $top['class']; + } else { + $class = current(array_filter([ + 'Plugin' . Core::toCamelCase($top['type']), + Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($top['type']) + ], 'class_exists')); + } + if (count($this->stack)) { + $this->curBlock = &$this->stack[count($this->stack) - 1]; + $this->push(call_user_func(array( + $class, + 'postProcessing' + ), $this, $top['params'], '', '', $top['buffer']), 0); + } else { + $null = null; + $this->curBlock = &$null; + $output = call_user_func( + array( + $class, + 'postProcessing' + ), $this, $top['params'], '', '', $top['buffer'] + ); + } + + if ($top['type'] === $type) { + break 2; + } + } + + throw new CompilationException($this, 'Syntax malformation, a block of type "' . $type . '" was closed but was not opened'); + } + + return $output; + } + + /** + * Returns a reference to the first block of the given type encountered and + * optionally closes all blocks until it finds it + * this is mainly used by {else} plugins to close everything that was opened + * between their parent and themselves. + * + * @param string $type the block type (name) + * @param bool $closeAlong whether to close all blocks encountered while going down the block stack or not + * + * @return mixed &array the array is as such: array('type'=>pluginName, 'params'=>parameter array, + * 'custom'=>bool defining whether it's a custom plugin or not, for internal use) + * @throws CompilationException + */ + public function &findBlock($type, $closeAlong = false) + { + if ($closeAlong === true) { + while ($b = end($this->stack)) { + if ($b['type'] === $type) { + return $this->stack[key($this->stack)]; + } + $this->push($this->removeTopBlock(), 0); + } + } else { + end($this->stack); + while ($b = current($this->stack)) { + if ($b['type'] === $type) { + return $this->stack[key($this->stack)]; + } + prev($this->stack); + } + } + + throw new CompilationException($this, 'A parent block of type "' . $type . '" is required and can not be found'); + } + + /** + * Returns a reference to the current block array. + * + * @return array the array is as such: array('type'=>pluginName, 'params'=>parameter array, + * 'custom'=>bool defining whether it's a custom plugin or not, for internal use) + */ + public function &getCurrentBlock() + { + return $this->curBlock; + } + + /** + * Removes the block at the top of the stack and calls its postProcessing() method. + * + * @return string the postProcessing() method's output + * @throws CompilationException + */ + public function removeTopBlock() + { + if ($this->debug) { + echo 'Compiler::' . __FUNCTION__ . "\n"; + } + + $o = array_pop($this->stack); + if ($o === null) { + throw new CompilationException($this, 'Syntax malformation, a block of unknown type was closed but was not opened.'); + } + if ($o['custom']) { + $class = $o['class']; + } else { + $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($o['type']); + } + + $this->curBlock = &$this->stack[count($this->stack) - 1]; + + return call_user_func(array($class, 'postProcessing'), $this, $o['params'], '', '', $o['buffer']); + } + + /** + * Returns the compiled parameters (for example a variable's compiled parameter will be "$this->scope['key']") out + * of the given parameter array. + * + * @param array $params parameter array + * + * @return array filtered parameters + */ + public function getCompiledParams(array $params) + { + foreach ($params as $k => $p) { + if (is_array($p)) { + $params[$k] = $p[0]; + } + } + + return $params; + } + + /** + * Returns the real parameters (for example a variable's real parameter will be its key, etc) out of the given + * parameter array. + * + * @param array $params parameter array + * + * @return array filtered parameters + */ + public function getRealParams(array $params) + { + foreach ($params as $k => $p) { + if (is_array($p)) { + $params[$k] = $p[1]; + } + } + + return $params; + } + + /** + * Returns the token of each parameter out of the given parameter array. + * + * @param array $params parameter array + * + * @return array tokens + */ + public function getParamTokens(array $params) + { + foreach ($params as $k => $p) { + if (is_array($p)) { + $params[$k] = isset($p[2]) ? $p[2] : 0; + } + } + + return $params; + } + + /** + * Entry point of the parser, it redirects calls to other parse* functions. + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by + * default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, + * or null by default + * + * @return string parsed values + * @throws CompilationException + */ + protected function parse($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) + { + if ($this->debug) { + echo 'Compiler::' . __FUNCTION__ . "\n"; + } + + if ($to === null) { + $to = strlen($in); + } + $first = substr($in, $from, 1); + + if ($first === false) { + throw new CompilationException($this, 'Unexpected EOF, a template tag was not closed'); + } + + while ($first === ' ' || $first === "\n" || $first === "\t" || $first === "\r") { + if ($curBlock === 'root' && substr($in, $from, strlen($this->rd)) === $this->rd) { + // end template tag + $pointer += strlen($this->rd); + if ($this->debug) { + echo 'TEMPLATE PARSING ENDED' . "\n"; + } + + return false; + } + ++ $from; + if ($pointer !== null) { + ++ $pointer; + } + if ($from >= $to) { + if (is_array($parsingParams)) { + return $parsingParams; + } else { + return ''; + } + } + $first = $in[$from]; + } + + $substr = substr($in, $from, $to - $from); + + if ($this->debug) { + echo 'PARSE CALL : PARSING "' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . '" @ ' . $from . ':' . $to . ' in ' . $curBlock . ' : pointer=' . $pointer . "\n"; + } + $parsed = ''; + + if ($curBlock === 'root' && $first === '*') { + $src = $this->getTemplateSource(); + $startpos = $this->getPointer() - strlen($this->ld); + if (substr($src, $startpos, strlen($this->ld)) === $this->ld) { + if ($startpos > 0) { + do { + $char = substr($src, -- $startpos, 1); + if ($char == "\n") { + ++ $startpos; + $whitespaceStart = true; + break; + } + } + while ($startpos > 0 && ($char == ' ' || $char == "\t")); + } + + if (!isset($whitespaceStart)) { + $startpos = $this->getPointer(); + } else { + $pointer -= $this->getPointer() - $startpos; + } + + if ($this->allowNestedComments && strpos($src, $this->ld . '*', $this->getPointer()) !== false) { + $comOpen = $this->ld . '*'; + $comClose = '*' . $this->rd; + $level = 1; + $ptr = $this->getPointer(); + + while ($level > 0 && $ptr < strlen($src)) { + $open = strpos($src, $comOpen, $ptr); + $close = strpos($src, $comClose, $ptr); + + if ($open !== false && $close !== false) { + if ($open < $close) { + $ptr = $open + strlen($comOpen); + ++ $level; + } else { + $ptr = $close + strlen($comClose); + -- $level; + } + } elseif ($open !== false) { + $ptr = $open + strlen($comOpen); + ++ $level; + } elseif ($close !== false) { + $ptr = $close + strlen($comClose); + -- $level; + } else { + $ptr = strlen($src); + } + } + $endpos = $ptr - strlen('*' . $this->rd); + } else { + $endpos = strpos($src, '*' . $this->rd, $startpos); + if ($endpos == false) { + throw new CompilationException($this, 'Un-ended comment'); + } + } + $pointer += $endpos - $startpos + strlen('*' . $this->rd); + if (isset($whitespaceStart) && preg_match('#^[\t ]*\r?\n#', substr($src, $endpos + strlen('*' . $this->rd)), $m)) { + $pointer += strlen($m[0]); + $this->curBlock['buffer'] = substr($this->curBlock['buffer'], 0, strlen($this->curBlock['buffer']) - ($this->getPointer() - $startpos - strlen($this->ld))); + } + + return false; + } + } + + if ($first === '$') { + // var + $out = $this->parseVar($in, $from, $to, $parsingParams, $curBlock, $pointer); + $parsed = 'var'; + } elseif ($first === '%' && preg_match('#^%[a-z_\\\\]#i', $substr)) { + // Short constant + $out = $this->parseConst($in, $from, $to, $parsingParams, $curBlock, $pointer); + } elseif (($first === '"' || $first === "'") && !(is_array($parsingParams) && preg_match('#^([\'"])[a-z0-9_]+\1\s*=>?(?:\s+|[^=])#i', $substr))) { + // string + $out = $this->parseString($in, $from, $to, $parsingParams, $curBlock, $pointer); + } elseif (preg_match('/^\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?(' . (is_array($parsingParams) || $curBlock != 'root' ? '' : '\s+[^(]|') . '\s*\(|\s*' . $this->rdr . '|\s*;)/i', $substr)) { + // func + $out = $this->parseFunction($in, $from, $to, $parsingParams, $curBlock, $pointer); + $parsed = 'func'; + } elseif ($first === ';') { + // instruction end + if ($this->debug) { + echo 'END OF INSTRUCTION' . "\n"; + } + if ($pointer !== null) { + ++ $pointer; + } + + return $this->parse($in, $from + 1, $to, false, 'root', $pointer); + } elseif ($curBlock === 'root' && preg_match('#^/([a-z_][a-z0-9_]*)?#i', $substr, $match)) { + // close block + if (!empty($match[1]) && $match[1] == 'else') { + throw new CompilationException($this, 'Else blocks must not be closed explicitly, they are automatically closed when their parent block is closed'); + } + if (!empty($match[1]) && $match[1] == 'elseif') { + throw new CompilationException($this, 'Elseif blocks must not be closed explicitly, they are automatically closed when their parent block is closed or a new else/elseif block is declared after them'); + } + if ($pointer !== null) { + $pointer += strlen($match[0]); + } + if (empty($match[1])) { + if ($this->curBlock['type'] == 'else' || $this->curBlock['type'] == 'elseif') { + $pointer -= strlen($match[0]); + } + if ($this->debug) { + echo 'TOP BLOCK CLOSED' . "\n"; + } + + return $this->removeTopBlock(); + } else { + if ($this->debug) { + echo 'BLOCK OF TYPE ' . $match[1] . ' CLOSED' . "\n"; + } + + return $this->removeBlock($match[1]); + } + } elseif ($curBlock === 'root' && substr($substr, 0, strlen($this->rd)) === $this->rd) { + // end template tag + if ($this->debug) { + echo 'TAG PARSING ENDED' . "\n"; + } + $pointer += strlen($this->rd); + + return false; + } elseif (is_array($parsingParams) && preg_match('#^(([\'"]?)[a-z0-9_]+\2\s*=' . ($curBlock === 'array' ? '>?' : '') . ')(?:\s+|[^=]).*#i', $substr, $match)) { + // named parameter + if ($this->debug) { + echo 'NAMED PARAM FOUND' . "\n"; + } + $len = strlen($match[1]); + while (substr($in, $from + $len, 1) === ' ') { + ++ $len; + } + if ($pointer !== null) { + $pointer += $len; + } + + $output = array( + trim($match[1], " \t\r\n=>'\""), + $this->parse($in, $from + $len, $to, false, 'namedparam', $pointer) + ); + + $parsingParams[] = $output; + + return $parsingParams; + } elseif (preg_match('#^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*::\$[a-z0-9_]+)#i', $substr, $match)) { + // static member access + $parsed = 'var'; + if (is_array($parsingParams)) { + $parsingParams[] = array($match[1], $match[1]); + $out = $parsingParams; + } else { + $out = $match[1]; + } + $pointer += strlen($match[1]); + } elseif ($substr !== '' && (is_array($parsingParams) || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'expression')) { + // unquoted string, bool or number + $out = $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer); + } else { + // parse error + throw new CompilationException($this, 'Parse error in "' . substr($in, $from, $to - $from) . '"'); + } + + if (empty($out)) { + return ''; + } + + $substr = substr($in, $pointer, $to - $pointer); + + // var parsed, check if any var-extension applies + if ($parsed === 'var') { + if (preg_match('#^\s*([/%+*-])\s*([a-z0-9]|\$)#i', $substr, $match)) { + if ($this->debug) { + echo 'PARSING POST-VAR EXPRESSION ' . $substr . "\n"; + } + // parse expressions + $pointer += strlen($match[0]) - 1; + if (is_array($parsingParams)) { + if ($match[2] == '$') { + $expr = $this->parseVar($in, $pointer, $to, array(), $curBlock, $pointer); + } else { + $expr = $this->parse($in, $pointer, $to, array(), 'expression', $pointer); + } + $out[count($out) - 1][0] .= $match[1] . $expr[0][0]; + $out[count($out) - 1][1] .= $match[1] . $expr[0][1]; + } else { + if ($match[2] == '$') { + $expr = $this->parseVar($in, $pointer, $to, false, $curBlock, $pointer); + } else { + $expr = $this->parse($in, $pointer, $to, false, 'expression', $pointer); + } + if (is_array($out) && is_array($expr)) { + $out[0] .= $match[1] . $expr[0]; + $out[1] .= $match[1] . $expr[1]; + } elseif (is_array($out)) { + $out[0] .= $match[1] . $expr; + $out[1] .= $match[1] . $expr; + } elseif (is_array($expr)) { + $out .= $match[1] . $expr[0]; + } else { + $out .= $match[1] . $expr; + } + } + } elseif ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s', $substr, $match)) { + if ($this->debug) { + echo 'PARSING POST-VAR ASSIGNMENT ' . $substr . "\n"; + } + // parse assignment + $value = $match[2]; + $operator = trim($match[1]); + if (substr($value, 0, 1) == '=') { + throw new CompilationException($this, 'Unexpected "=" in ' . $substr . ''); + } + + if ($pointer !== null) { + $pointer += strlen($match[1]); + } + + if ($operator !== '++' && $operator !== '--') { + $parts = array(); + $ptr = 0; + $parts = $this->parse($value, 0, strlen($value), $parts, 'condition', $ptr); + $pointer += $ptr; + + // load if plugin + try { + $this->getPluginType('if'); + } + catch (Exception $e) { + throw new CompilationException($this, 'Assignments require the "if" plugin to be accessible'); + } + + $parts = $this->mapParams($parts, array(Core::NAMESPACE_PLUGINS_BLOCKS . 'PluginIf', 'init'), 1); + $tokens = $this->getParamTokens($parts); + $parts = $this->getCompiledParams($parts); + + $value = PluginIf::replaceKeywords($parts['*'], $tokens['*'], $this); + $echo = ''; + } else { + $value = array(); + $echo = 'echo '; + } + + if ($this->autoEscape) { + $out = preg_replace('#\(is_string\(\$tmp=(.+?)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#', '$1', $out); + } + $out = self::PHP_OPEN . $echo . $out . $operator . implode(' ', $value) . self::PHP_CLOSE; + } elseif ($curBlock === 'array' && is_array($parsingParams) && preg_match('#^(\s*=>?\s*)#', $substr, $match)) { + // parse namedparam with var as name (only for array) + if ($this->debug) { + echo 'VARIABLE NAMED PARAM (FOR ARRAY) FOUND' . "\n"; + } + $len = strlen($match[1]); + $var = $out[count($out) - 1]; + $pointer += $len; + + $output = array($var[0], $this->parse($substr, $len, null, false, 'namedparam', $pointer)); + + $parsingParams[] = $output; + + return $parsingParams; + } + } + + if ($curBlock !== 'modifier' && ($parsed === 'func' || $parsed === 'var') && preg_match('#^(\|@?[a-z0-9_]+(:.*)?)+#i', $substr, $match)) { + // parse modifier on funcs or vars + $srcPointer = $pointer; + if (is_array($parsingParams)) { + $tmp = $this->replaceModifiers( + array( + null, + null, + $out[count($out) - 1][0], + $match[0] + ), $curBlock, $pointer + ); + $out[count($out) - 1][0] = $tmp; + $out[count($out) - 1][1] .= substr($substr, $srcPointer, $srcPointer - $pointer); + } else { + $out = $this->replaceModifiers(array(null, null, $out, $match[0]), $curBlock, $pointer); + } + } + + // func parsed, check if any func-extension applies + if ($parsed === 'func' && preg_match('#^->[a-z0-9_]+(\s*\(.+|->[a-z_].*)?#is', $substr, $match)) { + // parse method call or property read + $ptr = 0; + + if (is_array($parsingParams)) { + $output = $this->parseMethodCall($out[count($out) - 1][1], $match[0], $curBlock, $ptr); + + $out[count($out) - 1][0] = $output; + $out[count($out) - 1][1] .= substr($match[0], 0, $ptr); + } else { + $out = $this->parseMethodCall($out, $match[0], $curBlock, $ptr); + } + + $pointer += $ptr; + } + + if ($curBlock === 'root' && substr($out, 0, strlen(self::PHP_OPEN)) !== self::PHP_OPEN) { + return self::PHP_OPEN . 'echo ' . $out . ';' . self::PHP_CLOSE; + } + + return $out; + } + + /** + * Parses a function call. + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by + * default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, + * or null by default + * + * @return string parsed values + * @throws CompilationException + * @throws Exception + * @throws SecurityException + */ + protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) + { + $output = ''; + $cmdstr = substr($in, $from, $to - $from); + preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\s*' . $this->rdr . '|\s*;)?/i', $cmdstr, $match); + + if (empty($match[1])) { + throw new CompilationException($this, 'Parse error, invalid function name : ' . substr($cmdstr, 0, 15)); + } + + $func = $match[1]; + + if (!empty($match[2])) { + $cmdstr = $match[1]; + } + + if ($this->debug) { + echo 'FUNC FOUND (' . $func . ')' . "\n"; + } + + $paramsep = ''; + + if (is_array($parsingParams) || $curBlock != 'root') { + $paramspos = strpos($cmdstr, '('); + $paramsep = ')'; + } elseif (preg_match_all('#^\s*[\\\\:a-z0-9_]+(\s*\(|\s+[^(])#i', $cmdstr, $match, PREG_OFFSET_CAPTURE)) { + $paramspos = $match[1][0][1]; + $paramsep = substr($match[1][0][0], - 1) === '(' ? ')' : ''; + if ($paramsep === ')') { + $paramspos += strlen($match[1][0][0]) - 1; + if (substr($cmdstr, 0, 2) === 'if' || substr($cmdstr, 0, 6) === 'elseif') { + $paramsep = ''; + if (strlen($match[1][0][0]) > 1) { + -- $paramspos; + } + } + } + } else { + $paramspos = false; + } + + $state = 0; + + if ($paramspos === false) { + $params = array(); + + if ($curBlock !== 'root') { + return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer); + } + } else { + if ($curBlock === 'condition') { + // load if plugin + $this->getPluginType('if'); + + if (PluginIf::replaceKeywords(array($func), array(self::T_UNQUOTED_STRING), $this) !== array($func)) { + return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer); + } + } + $whitespace = strlen(substr($cmdstr, strlen($func), $paramspos - strlen($func))); + $paramstr = substr($cmdstr, $paramspos + 1); + if (substr($paramstr, - 1, 1) === $paramsep) { + $paramstr = substr($paramstr, 0, - 1); + } + + if (strlen($paramstr) === 0) { + $params = array(); + $paramstr = ''; + } else { + $ptr = 0; + $params = array(); + if ($func === 'empty') { + $params = $this->parseVar($paramstr, $ptr, strlen($paramstr), $params, 'root', $ptr); + } else { + while ($ptr < strlen($paramstr)) { + while (true) { + if ($ptr >= strlen($paramstr)) { + break 2; + } + + if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === ')') { + if ($this->debug) { + echo 'PARAM PARSING ENDED, ")" FOUND, POINTER AT ' . $ptr . "\n"; + } + break 2; + } elseif ($paramstr[$ptr] === ';') { + ++ $ptr; + if ($this->debug) { + echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT ' . $ptr . "\n"; + } + break 2; + } elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') { + if ($this->debug) { + echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT ' . $ptr . "\n"; + } + break 2; + } elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) { + if ($this->debug) { + echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT ' . $ptr . "\n"; + } + break 2; + } + + if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === ',' || $paramstr[$ptr] === "\r" || $paramstr[$ptr] === "\n" || $paramstr[$ptr] === "\t") { + ++ $ptr; + } else { + break; + } + } + + if ($this->debug) { + echo 'FUNC START PARAM PARSING WITH POINTER AT ' . $ptr . "\n"; + } + + if ($func === 'if' || $func === 'elseif' || $func === 'tif') { + $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'condition', $ptr); + } elseif ($func === 'array') { + $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'array', $ptr); + } else { + $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'function', $ptr); + } + + if ($this->debug) { + echo 'PARAM PARSED, POINTER AT ' . $ptr . ' (' . substr($paramstr, $ptr - 1, 3) . ')' . "\n"; + } + } + } + $paramstr = substr($paramstr, 0, $ptr); + $state = 0; + foreach ($params as $k => $p) { + if (is_array($p) && is_array($p[1])) { + $state |= 2; + } else { + if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m) && $func !== 'array') { + $params[$k] = array($m[2], array('true', 'true')); + } else { + if ($state & 2 && $func !== 'array') { + throw new CompilationException($this, 'You can not use an unnamed parameter after a named one'); + } + $state |= 1; + } + } + } + } + } + + if ($pointer !== null) { + $pointer += (isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func) + (isset($whitespace) ? $whitespace : 0); + if ($this->debug) { + echo 'FUNC ADDS ' . ((isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func)) . ' TO POINTER' . "\n"; + } + } + + if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) { + // handle static method calls with security policy + if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) { + throw new SecurityException('Call to a disallowed php function : ' . $func); + } + $pluginType = Core::NATIVE_PLUGIN; + } else { + $pluginType = $this->getPluginType($func); + } + + // Blocks plugin + if ($pluginType & Core::BLOCK_PLUGIN) { + if ($curBlock !== 'root' || is_array($parsingParams)) { + throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments'); + } + if ($pluginType & Core::CUSTOM_PLUGIN) { + return $this->addCustomBlock($func, $params, $state); + } else { + return $this->addBlock($func, $params, $state); + } + } elseif ($pluginType & Core::SMARTY_BLOCK) { + if ($curBlock !== 'root' || is_array($parsingParams)) { + throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments'); + } + + if ($state & 2) { + array_unshift($params, array('__functype', array($pluginType, $pluginType))); + array_unshift($params, array('__funcname', array($func, $func))); + } else { + array_unshift($params, array($pluginType, $pluginType)); + array_unshift($params, array($func, $func)); + } + + return $this->addBlock('smartyinterface', $params, $state); + } + + // Native & Smarty plugins + if ($pluginType & Core::NATIVE_PLUGIN || $pluginType & Core::SMARTY_FUNCTION || $pluginType & Core::SMARTY_BLOCK) { + $params = $this->mapParams($params, null, $state); + } // PHP class plugin + elseif ($pluginType & Core::CLASS_PLUGIN) { + if ($pluginType & Core::CUSTOM_PLUGIN) { + $params = $this->mapParams( + $params, array( + $this->customPlugins[$func]['class'], + $this->customPlugins[$func]['function'] + ), $state); + } else { + if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) { + $params = $this->mapParams($params, array( + 'Plugin' . Core::toCamelCase($func), + ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process' + ), $state); + } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) { + $params = $this->mapParams($params, array( + Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func), + ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process' + ), $state); + } else { + $params = $this->mapParams($params, array( + Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func), + ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process' + ), $state); + } + } + } // PHP function plugin + elseif ($pluginType & Core::FUNC_PLUGIN) { + if ($pluginType & Core::CUSTOM_PLUGIN) { + $params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state); + } else { + // Custom plugin + if (function_exists('Plugin' . Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? + 'Compile' : '')) !== false) { + $params = $this->mapParams($params, 'Plugin' . Core::toCamelCase($func) . (($pluginType & + Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state); + } // Builtin helper plugin + elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . ( + ($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '')) !== false) { + $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase + ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state); + } // Builtin function plugin + else { + $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase + ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state); + } + } + } // Smarty modifier + elseif ($pluginType & Core::SMARTY_MODIFIER) { + $output = 'smarty_modifier_' . $func . '(' . implode(', ', $params) . ')'; + } // Proxy plugin + elseif ($pluginType & Core::PROXY_PLUGIN) { + $params = $this->mapParams($params, $this->getCore()->getPluginProxy()->getCallback($func), $state); + } // Template plugin + elseif ($pluginType & Core::TEMPLATE_PLUGIN) { + // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values)) + $map = array(); + foreach ($this->templatePlugins[$func]['params'] as $param => $defValue) { + if ($param == 'rest') { + $param = '*'; + } + $hasDefault = $defValue !== null; + if ($defValue === 'null') { + $defValue = null; + } elseif ($defValue === 'false') { + $defValue = false; + } elseif ($defValue === 'true') { + $defValue = true; + } elseif (preg_match('#^([\'"]).*?\1$#', $defValue)) { + $defValue = substr($defValue, 1, - 1); + } + $map[] = array($param, $hasDefault, $defValue); + } + + $params = $this->mapParams($params, null, $state, $map); + } + + // only keep php-syntax-safe values for non-block plugins + $tokens = array(); + foreach ($params as $k => $p) { + $tokens[$k] = isset($p[2]) ? $p[2] : 0; + $params[$k] = $p[0]; + } + + // Native plugin + if ($pluginType & Core::NATIVE_PLUGIN) { + if ($func === 'do') { + $output = ''; + if (isset($params['*'])) { + $output = implode(';', $params['*']) . ';'; + } + + if (is_array($parsingParams) || $curBlock !== 'root') { + throw new CompilationException($this, 'Do can not be used inside another function or block'); + } + + return self::PHP_OPEN . $output . self::PHP_CLOSE; + } else { + if (isset($params['*'])) { + $output = $func . '(' . implode(', ', $params['*']) . ')'; + } else { + $output = $func . '()'; + } + } + } // Block class OR Function class + elseif ($pluginType & Core::CLASS_PLUGIN || ($pluginType & Core::FUNC_PLUGIN && $pluginType & Core::CLASS_PLUGIN)) { + if ($pluginType & Core::COMPILABLE_PLUGIN) { + if ($pluginType & Core::CUSTOM_PLUGIN) { + $callback = $this->customPlugins[$func]['callback']; + if (!is_array($callback)) { + if (!method_exists($callback, 'compile')) { + throw new Exception('Custom plugin ' . $func . ' must implement the "compile" method to be compilable, or you should provide a full callback to the method to use'); + } + if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) { + $funcCompiler = array($callback, 'compile'); + } else { + $funcCompiler = array(new $callback(), 'compile'); + } + } else { + $funcCompiler = $callback; + } + } else { + if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) { + $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile'); + } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) { + $funcCompiler = array(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func), 'compile'); + } else { + $funcCompiler = array( + Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func), + 'compile' + ); + } + array_unshift($params, $this); + } + // @TODO: Is it a real fix ? + if ($func === 'tif') { + $params[] = $tokens; + } + $output = call_user_func_array($funcCompiler, $params); + } else { + $params = self::implode_r($params); + if ($pluginType & Core::CUSTOM_PLUGIN) { + $callback = $this->customPlugins[$func]['callback']; + if (!is_array($callback)) { + if (!method_exists($callback, 'process')) { + throw new Exception('Custom plugin ' . $func . ' must implement the "process" method to be usable, or you should provide a full callback to the method to use'); + } + if (is_object($callback)) { + $callback = get_class($callback); + } + if (($ref = new ReflectionMethod($callback, 'process')) && $ref->isStatic()) { + $output = 'call_user_func(array(\'' . $callback . '\', \'process\'), ' . $params . ')'; + } else { + $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback . '\'), \'process\'), ' . $params . ')'; + } + } elseif (is_object($callback[0])) { + $output = 'call_user_func(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), ' . $params . ')'; + } elseif (($ref = new ReflectionMethod($callback[0], $callback[1])) && $ref->isStatic()) { + $output = 'call_user_func(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), ' . $params . ')'; + } else { + $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback[0] . '\'), \'' . $callback[1] . '\'), ' . $params . ')'; + } + if (empty($params)) { + $output = substr($output, 0, - 3) . ')'; + } + } else { + if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) { + $output = '$this->classCall(\'Plugin' . $func . '\', array(' . $params . '))'; + } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) { + $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', + array(' . $params . '))'; + } else { + $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))'; + } + } + } + } // Function plugin only (cannot be a class) + elseif ($pluginType & Core::FUNC_PLUGIN) { + if ($pluginType & Core::COMPILABLE_PLUGIN) { + if ($pluginType & Core::CUSTOM_PLUGIN) { + $funcCompiler = $this->customPlugins[$func]['callback']; + } else { + // Custom plugin + if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) { + $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile'; + } // Builtin helper plugin + elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . 'Compile') !== false) { + $funcCompiler = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . + 'Compile'; + } // Builtin function plugin + else { + $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . + 'Compile'; + } + } + array_unshift($params, $this); + // @TODO: Is it a real fix ? + if ($func === 'tif') { + $params[] = $tokens; + } + $output = call_user_func_array($funcCompiler, $params); + } else { + array_unshift($params, '$this'); + $params = self::implode_r($params); + if ($pluginType & Core::CUSTOM_PLUGIN) { + $callback = $this->customPlugins[$func]['callback']; + if ($callback instanceof Closure) { + $output = 'call_user_func($this->getCustomPlugin(\'' . $func . '\'), ' . $params . ')'; + } else { + $output = 'call_user_func(\'' . $callback . '\', ' . $params . ')'; + } + } else { + // Custom plugin + if (function_exists('Plugin' . Core::toCamelCase($func)) !== false) { + $output = 'Plugin' . Core::toCamelCase($func) . '(' . $params . + ')'; + } // Builtin helper plugin + elseif(function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== + false) { + $output = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . '(' . + $params . ')'; + } // Builtin function plugin + else { + $output = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '(' . + $params . ')'; + } + } + } + } // Proxy plugin + elseif ($pluginType & Core::PROXY_PLUGIN) { + $output = call_user_func(array($this->getCore()->getPluginProxy(), 'getCode'), $func, $params); + } // Smarty function (@deprecated) + elseif ($pluginType & Core::SMARTY_FUNCTION) { + $params = ''; + if (isset($params['*'])) { + $params = self::implode_r($params['*'], true); + } + + if ($pluginType & Core::CUSTOM_PLUGIN) { + $callback = $this->customPlugins[$func]['callback']; + if (is_array($callback)) { + if (is_object($callback[0])) { + $output = 'call_user_func_array(array($this->getCustomPlugin(\'' . $func . '\'), \'' . $callback[1] . '\'), array(array(' . $params . '), $this))'; + } else { + $output = 'call_user_func_array(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(array(' . $params . '), $this))'; + } + } else { + $output = $callback . '(array(' . $params . '), $this)'; + } + } else { + $output = 'smarty_function_' . $func . '(array(' . $params . '), $this)'; + } + } // Template plugin + elseif ($pluginType & Core::TEMPLATE_PLUGIN) { + array_unshift($params, '$this'); + $params = self::implode_r($params); + $output = 'Plugin' . Core::toCamelCase($func) . + $this->templatePlugins[$func]['uuid'] . '(' . $params . ')'; + $this->templatePlugins[$func]['called'] = true; + } + + if (is_array($parsingParams)) { + $parsingParams[] = array($output, $output); + + return $parsingParams; + } elseif ($curBlock === 'namedparam') { + return array($output, $output); + } + + return $output; + } + + /** + * Parses a string. + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by + * default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, + * or null by default + * + * @return string parsed values + * @throws CompilationException + */ + protected function parseString($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) + { + $substr = substr($in, $from, $to - $from); + $first = $substr[0]; + + if ($this->debug) { + echo 'STRING FOUND (in ' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . ')' . "\n"; + } + $strend = false; + $o = $from + 1; + while ($strend === false) { + $strend = strpos($in, $first, $o); + if ($strend === false) { + throw new CompilationException($this, 'Unfinished string, started with ' . substr($in, $from, $to - $from)); + } + if (substr($in, $strend - 1, 1) === '\\') { + $o = $strend + 1; + $strend = false; + } + } + if ($this->debug) { + echo 'STRING DELIMITED: ' . substr($in, $from, $strend + 1 - $from) . "\n"; + } + + $srcOutput = substr($in, $from, $strend + 1 - $from); + + if ($pointer !== null) { + $pointer += strlen($srcOutput); + } + + $output = $this->replaceStringVars($srcOutput, $first); + + // handle modifiers + if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i', substr($substr, $strend + 1 - $from), $match)) { + $modstr = $match[1]; + + if ($curBlock === 'root' && substr($modstr, - 1) === '}') { + $modstr = substr($modstr, 0, - 1); + } + $modstr = str_replace('\\' . $first, $first, $modstr); + $ptr = 0; + $output = $this->replaceModifiers(array(null, null, $output, $modstr), 'string', $ptr); + + $strend += $ptr; + if ($pointer !== null) { + $pointer += $ptr; + } + $srcOutput .= substr($substr, $strend + 1 - $from, $ptr); + } + + if (is_array($parsingParams)) { + $parsingParams[] = array($output, substr($srcOutput, 1, - 1)); + + return $parsingParams; + } elseif ($curBlock === 'namedparam') { + return array($output, substr($srcOutput, 1, - 1)); + } + + return $output; + } + + /** + * Parses a constant. + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by + * default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, + * or null by default + * + * @return string parsed values + * @throws CompilationException + */ + protected function parseConst($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) + { + $substr = substr($in, $from, $to - $from); + + if ($this->debug) { + echo 'CONST FOUND : ' . $substr . "\n"; + } + + if (!preg_match('#^%([\\\\a-z0-9_:]+)#i', $substr, $m)) { + throw new CompilationException($this, 'Invalid constant'); + } + + if ($pointer !== null) { + $pointer += strlen($m[0]); + } + + $output = $this->parseConstKey($m[1], $curBlock); + + if (is_array($parsingParams)) { + $parsingParams[] = array($output, $m[1]); + + return $parsingParams; + } elseif ($curBlock === 'namedparam') { + return array($output, $m[1]); + } + + return $output; + } + + /** + * Parses a constant. + * + * @param string $key the constant to parse + * @param string $curBlock the current parser-block being processed + * + * @return string parsed constant + */ + protected function parseConstKey($key, $curBlock) + { + $key = str_replace('\\\\', '\\', $key); + + if ($this->securityPolicy !== null && $this->securityPolicy->getConstantHandling() === SecurityPolicy::CONST_DISALLOW) { + return 'null'; + } + + if ($curBlock !== 'root') { + return '(defined("' . $key . '") ? ' . $key . ' : null)'; + } + + return $key; + } + + /** + * Parses a variable. + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by + * default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, + * or null by default + * + * @return string parsed values + * @throws CompilationException + */ + protected function parseVar($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) + { + $substr = substr($in, $from, $to - $from); + + // var key + $varRegex = '(\\$?\\.?[a-z0-9\\\\_:]*(?:(?:(?:\\.|->)(?:[a-z0-9\\\\_:]+|(?R))|\\[(?:[a-z0-9\\\\_:]+|(?R)|(["\'])[^\\2]*?\\2)\\]))*)'; + // method call + $methodCall = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'expression' || $curBlock === 'delimited_string' ? '(\(.*)?' : '()'); + // simple math expressions + $simpleMathExpressions = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'delimited_string' ? '((?:(?:[+\/*%=-])(?:(?_:-]+(?:\([^)]*\))?|(?'))); + $key = substr($match[1], 1, strrpos($match[1], '->') - 1); + $methodCall = substr($match[1], strrpos($match[1], '->')) . $match[3]; + } + + if ($hasModifiers) { + $matchedLength -= strlen($match[5]); + } + + if ($pointer !== null) { + $pointer += $matchedLength; + } + + // replace useless brackets by dot accessed vars and strip enclosing quotes if present + $key = preg_replace('#\[(["\']?)([^$%\[.>-]+)\1\]#', '.$2', $key); + + if ($this->debug) { + if ($hasMethodCall) { + echo 'METHOD CALL FOUND : $' . $key . substr($methodCall, 0, 30) . "\n"; + } else { + echo 'VAR FOUND : $' . $key . "\n"; + } + } + + $key = str_replace('"', '\\"', $key); + + $cnt = substr_count($key, '$'); + if ($cnt > 0) { + $uid = 0; + $parsed = array($uid => ''); + $current = &$parsed; + $curTxt = &$parsed[$uid ++]; + $tree = array(); + $chars = str_split($key, 1); + $inSplittedVar = false; + $bracketCount = 0; + + while (($char = array_shift($chars)) !== null) { + if ($char === '[') { + if (count($tree) > 0) { + ++ $bracketCount; + } else { + $tree[] = &$current; + $current[$uid] = array($uid + 1 => ''); + $current = &$current[$uid ++]; + $curTxt = &$current[$uid ++]; + continue; + } + } elseif ($char === ']') { + if ($bracketCount > 0) { + -- $bracketCount; + } else { + $current = &$tree[count($tree) - 1]; + array_pop($tree); + if (current($chars) !== '[' && current($chars) !== false && current($chars) !== ']') { + $current[$uid] = ''; + $curTxt = &$current[$uid ++]; + } + continue; + } + } elseif ($char === '$') { + if (count($tree) == 0) { + $curTxt = &$current[$uid ++]; + $inSplittedVar = true; + } + } elseif (($char === '.' || $char === '-') && count($tree) == 0 && $inSplittedVar) { + $curTxt = &$current[$uid ++]; + $inSplittedVar = false; + } + + $curTxt .= $char; + } + unset($uid, $current, $curTxt, $tree, $chars); + + if ($this->debug) { + echo 'RECURSIVE VAR REPLACEMENT : ' . $key . "\n"; + } + + $key = $this->flattenVarTree($parsed); + + if ($this->debug) { + echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n"; + } + + $output = preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#', '$2$3', '$this->readVar("' . $key . '")'); + } else { + $output = $this->parseVarKey($key, $hasModifiers ? 'modifier' : $curBlock); + } + + + // methods + if ($hasMethodCall) { + $ptr = 0; + + $output = $this->parseMethodCall($output, $methodCall, $curBlock, $ptr); + + if ($pointer !== null) { + $pointer += $ptr; + } + $matchedLength += $ptr; + } + + if ($hasExpression) { + // expressions + preg_match_all('#(?:([+/*%=-])(=?-?[%$][a-z0-9\\\\.[\]>_:-]+(?:\([^)]*\))?|=?-?[0-9.,]+|\1))#i', $match[4], $expMatch); + foreach ($expMatch[1] as $k => $operator) { + if (substr($expMatch[2][$k], 0, 1) === '=') { + $assign = true; + if ($operator === '=') { + throw new CompilationException($this, 'Invalid expression ' . $substr . ', can not use "==" in expressions'); + } + if ($curBlock !== 'root') { + throw new CompilationException($this, 'Invalid expression ' . $substr . ', assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}'); + } + $operator .= '='; + $expMatch[2][$k] = substr($expMatch[2][$k], 1); + } + + if (substr($expMatch[2][$k], 0, 1) === '-' && strlen($expMatch[2][$k]) > 1) { + $operator .= '-'; + $expMatch[2][$k] = substr($expMatch[2][$k], 1); + } + if (($operator === '+' || $operator === '-') && $expMatch[2][$k] === $operator) { + $output = '(' . $output . $operator . $operator . ')'; + break; + } elseif (substr($expMatch[2][$k], 0, 1) === '$') { + $output = '(' . $output . ' ' . $operator . ' ' . $this->parseVar($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')'; + } elseif (substr($expMatch[2][$k], 0, 1) === '%') { + $output = '(' . $output . ' ' . $operator . ' ' . $this->parseConst($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')'; + } elseif (!empty($expMatch[2][$k])) { + $output = '(' . $output . ' ' . $operator . ' ' . str_replace(',', '.', $expMatch[2][$k]) . ')'; + } else { + throw new CompilationException($this, 'Unfinished expression ' . $substr . ', missing var or number after math operator'); + } + } + } + + if ($this->autoEscape === true && $curBlock !== 'condition') { + $output = '(is_string($tmp=' . $output . ') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)'; + } + + // handle modifiers + if ($curBlock !== 'modifier' && $hasModifiers) { + $ptr = 0; + $output = $this->replaceModifiers(array(null, null, $output, $match[5]), 'var', $ptr); + if ($pointer !== null) { + $pointer += $ptr; + } + $matchedLength += $ptr; + } + + if (is_array($parsingParams)) { + $parsingParams[] = array($output, $key); + + return $parsingParams; + } elseif ($curBlock === 'namedparam') { + return array($output, $key); + } elseif ($curBlock === 'string' || $curBlock === 'delimited_string') { + return array($matchedLength, $output); + } elseif ($curBlock === 'expression' || $curBlock === 'variable') { + return $output; + } elseif (isset($assign)) { + return self::PHP_OPEN . $output . ';' . self::PHP_CLOSE; + } + + return $output; + } else { + if ($curBlock === 'string' || $curBlock === 'delimited_string') { + return array(0, ''); + } + throw new CompilationException($this, 'Invalid variable name ' . $substr . ''); + } + } + + /** + * Parses any number of chained method calls/property reads. + * + * @param string $output the variable or whatever upon which the method are called + * @param string $methodCall method call source, starting at "->" + * @param string $curBlock the current parser-block being processed + * @param int $pointer a reference to a pointer that will be increased by the amount of characters parsed + * + * @return string parsed call(s)/read(s) + */ + protected function parseMethodCall($output, $methodCall, $curBlock, &$pointer) + { + $ptr = 0; + $len = strlen($methodCall); + + while ($ptr < $len) { + if (strpos($methodCall, '->', $ptr) === $ptr) { + $ptr += 2; + } + + if (in_array( + $methodCall[$ptr], array( + ';', + ',', + '/', + ' ', + "\t", + "\r", + "\n", + ')', + '+', + '*', + '%', + '=', + '-', + '|' + ) + ) || substr($methodCall, $ptr, strlen($this->rd)) === $this->rd + ) { + // break char found + break; + } + + if (!preg_match('/^([a-z0-9_]+)(\(.*?\))?/i', substr($methodCall, $ptr), $methMatch)) { + break; + } + + if (empty($methMatch[2])) { + // property + if ($curBlock === 'root') { + $output .= '->' . $methMatch[1]; + } else { + $output = '(($tmp = ' . $output . ') ? $tmp->' . $methMatch[1] . ' : null)'; + } + $ptr += strlen($methMatch[1]); + } else { + // method + if (substr($methMatch[2], 0, 2) === '()') { + $parsedCall = $methMatch[1] . '()'; + $ptr += strlen($methMatch[1]) + 2; + } else { + $parsedCall = $this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr); + } + if ($this->securityPolicy !== null) { + $argPos = strpos($parsedCall, '('); + $method = strtolower(substr($parsedCall, 0, $argPos)); + $args = substr($parsedCall, $argPos); + if ($curBlock === 'root') { + $output = '$this->getSecurityPolicy()->callMethod($this, ' . $output . ', ' . var_export($method, true) . ', array' . $args . ')'; + } else { + $output = '(($tmp = ' . $output . ') ? $this->getSecurityPolicy()->callMethod($this, $tmp, ' . var_export($method, true) . ', array' . $args . ') : null)'; + } + } else { + if ($curBlock === 'root') { + $output .= '->' . $parsedCall; + } else { + $output = '(($tmp = ' . $output . ') ? $tmp->' . $parsedCall . ' : null)'; + } + } + } + } + + $pointer += $ptr; + + return $output; + } + + /** + * Parses a constant variable (a variable that doesn't contain another variable) and preprocesses it to save + * runtime processing time. + * + * @param string $key the variable to parse + * @param string $curBlock the current parser-block being processed + * + * @return string parsed variable + */ + protected function parseVarKey($key, $curBlock) + { + if ($key === '') { + return '$this->scope'; + } + if (substr($key, 0, 1) === '.') { + $key = 'dwoo' . $key; + } + if (preg_match('#dwoo\.(get|post|server|cookies|session|env|request)((?:\.[a-z0-9_-]+)+)#i', $key, $m)) { + $global = strtoupper($m[1]); + if ($global === 'COOKIES') { + $global = 'COOKIE'; + } + $key = '$_' . $global; + foreach (explode('.', ltrim($m[2], '.')) as $part) { + $key .= '[' . var_export($part, true) . ']'; + } + if ($curBlock === 'root') { + $output = $key; + } else { + $output = '(isset(' . $key . ')?' . $key . ':null)'; + } + } elseif (preg_match('#dwoo\\.const\\.([a-z0-9\\\\_:]+)#i', $key, $m)) { + return $this->parseConstKey($m[1], $curBlock); + } elseif ($this->scope !== null) { + if (strstr($key, '.') === false && strstr($key, '[') === false && strstr($key, '->') === false) { + if ($key === 'dwoo') { + $output = '$this->globals'; + } elseif ($key === '_root' || $key === '__') { + $output = '$this->data'; + } elseif ($key === '_parent' || $key === '_') { + $output = '$this->readParentVar(1)'; + } elseif ($key === '_key') { + $output = '$tmp_key'; + } else { + if ($curBlock === 'root') { + $output = '$this->scope["' . $key . '"]'; + } else { + $output = '(isset($this->scope["' . $key . '"]) ? $this->scope["' . $key . '"] : null)'; + } + } + } else { + preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+|(\\\?[\'"])[^\3]*?\3)\]?#i', $key, $m); + + $i = $m[2][0]; + if ($i === '_parent' || $i === '_') { + $parentCnt = 0; + + while (true) { + ++ $parentCnt; + array_shift($m[2]); + array_shift($m[1]); + if (current($m[2]) === '_parent') { + continue; + } + break; + } + + $output = '$this->readParentVar(' . $parentCnt . ')'; + } else { + if ($i === 'dwoo') { + $output = '$this->globals'; + array_shift($m[2]); + array_shift($m[1]); + } elseif ($i === '_root' || $i === '__') { + // $output = '$this->data'; + $output = '$this->getData()'; + array_shift($m[2]); + array_shift($m[1]); + } elseif ($i === '_key') { + $output = '$tmp_key'; + } else { + $output = '$this->scope'; + } + + while (count($m[1]) && $m[1][0] !== '->') { + $m[2][0] = preg_replace('/(^\\\([\'"])|\\\([\'"])$)/x', '$2$3', $m[2][0]); + if (substr($m[2][0], 0, 1) == '"' || substr($m[2][0], 0, 1) == "'") { + $output .= '[' . $m[2][0] . ']'; + } else { + $output .= '["' . $m[2][0] . '"]'; + } + array_shift($m[2]); + array_shift($m[1]); + } + + if ($curBlock !== 'root') { + $output = '(isset(' . $output . ') ? ' . $output . ':null)'; + } + } + + if (count($m[2])) { + unset($m[0]); + $output = '$this->readVarInto(' . str_replace("\n", '', var_export($m, true)) . ', ' . $output . ', ' . ($curBlock == 'root' ? 'false' : 'true') . ')'; + } + } + } else { + preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+)\]?#i', $key, $m); + unset($m[0]); + $output = '$this->readVar(' . str_replace("\n", '', var_export($m, true)) . ')'; + } + + return $output; + } + + /** + * Flattens a variable tree, this helps in parsing very complex variables such as $var.foo[$foo.bar->baz].baz, + * it computes the contents of the brackets first and works out from there. + * + * @param array $tree the variable tree parsed by he parseVar() method that must be flattened + * @param bool $recursed leave that to false by default, it is only for internal use + * + * @return string flattened tree + */ + protected function flattenVarTree(array $tree, $recursed = false) + { + $out = $recursed ? '".$this->readVarInto(' : ''; + foreach ($tree as $bit) { + if (is_array($bit)) { + $out .= '.' . $this->flattenVarTree($bit, false); + } else { + $key = str_replace('"', '\\"', $bit); + + if (substr($key, 0, 1) === '$') { + $out .= '".' . $this->parseVar($key, 0, strlen($key), false, 'variable') . '."'; + } else { + $cnt = substr_count($key, '$'); + + if ($this->debug) { + echo 'PARSING SUBVARS IN : ' . $key . "\n"; + } + if ($cnt > 0) { + while (-- $cnt >= 0) { + if (isset($last)) { + $last = strrpos($key, '$', - (strlen($key) - $last + 1)); + } else { + $last = strrpos($key, '$'); + } + preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', substr($key, $last), $submatch); + + $len = strlen($submatch[0]); + $key = substr_replace( + $key, preg_replace_callback( + '#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', array( + $this, + 'replaceVarKeyHelper' + ), substr($key, $last, $len) + ), $last, $len + ); + if ($this->debug) { + echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n"; + } + } + unset($last); + + $out .= $key; + } else { + $out .= $key; + } + } + } + } + $out .= $recursed ? ', true)."' : ''; + + return $out; + } + + /** + * Helper function that parses a variable. + * + * @param array $match the matched variable, array(1=>"string match") + * + * @return string parsed variable + */ + protected function replaceVarKeyHelper($match) + { + return '".' . $this->parseVar($match[0], 0, strlen($match[0]), false, 'variable') . '."'; + } + + /** + * Parses various constants, operators or non-quoted strings. + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by + * default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, + * or null by default + * + * @return string parsed values + * @throws Exception + */ + protected function parseOthers($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) + { + $substr = substr($in, $from, $to - $from); + + $end = strlen($substr); + + if ($curBlock === 'condition') { + $breakChars = array( + '(', + ')', + ' ', + '||', + '&&', + '|', + '&', + '>=', + '<=', + '===', + '==', + '=', + '!==', + '!=', + '<<', + '<', + '>>', + '>', + '^', + '~', + ',', + '+', + '-', + '*', + '/', + '%', + '!', + '?', + ':', + $this->rd, + ';' + ); + } elseif ($curBlock === 'modifier') { + $breakChars = array(' ', ',', ')', ':', '|', "\r", "\n", "\t", ';', $this->rd); + } elseif ($curBlock === 'expression') { + $breakChars = array('/', '%', '+', '-', '*', ' ', ',', ')', "\r", "\n", "\t", ';', $this->rd); + } else { + $breakChars = array(' ', ',', ')', "\r", "\n", "\t", ';', $this->rd); + } + + $breaker = false; + foreach ($breakChars as $k => $char) { + $test = strpos($substr, $char); + if ($test !== false && $test < $end) { + $end = $test; + $breaker = $k; + } + } + + if ($curBlock === 'condition') { + if ($end === 0 && $breaker !== false) { + $end = strlen($breakChars[$breaker]); + } + } + + if ($end !== false) { + $substr = substr($substr, 0, $end); + } + + if ($pointer !== null) { + $pointer += strlen($substr); + } + + $src = $substr; + $substr = trim($substr); + + if (strtolower($substr) === 'false' || strtolower($substr) === 'no' || strtolower($substr) === 'off') { + if ($this->debug) { + echo 'BOOLEAN(FALSE) PARSED' . "\n"; + } + $substr = 'false'; + $type = self::T_BOOL; + } elseif (strtolower($substr) === 'true' || strtolower($substr) === 'yes' || strtolower($substr) === 'on') { + if ($this->debug) { + echo 'BOOLEAN(TRUE) PARSED' . "\n"; + } + $substr = 'true'; + $type = self::T_BOOL; + } elseif ($substr === 'null' || $substr === 'NULL') { + if ($this->debug) { + echo 'NULL PARSED' . "\n"; + } + $substr = 'null'; + $type = self::T_NULL; + } elseif (is_numeric($substr)) { + $substr = (float)$substr; + if ((int)$substr == $substr) { + $substr = (int)$substr; + } + $type = self::T_NUMERIC; + if ($this->debug) { + echo 'NUMBER (' . $substr . ') PARSED' . "\n"; + } + } elseif (preg_match('{^-?(\d+|\d*(\.\d+))\s*([/*%+-]\s*-?(\d+|\d*(\.\d+)))+$}', $substr)) { + if ($this->debug) { + echo 'SIMPLE MATH PARSED . "\n"'; + } + $type = self::T_MATH; + $substr = '(' . $substr . ')'; + } elseif ($curBlock === 'condition' && array_search($substr, $breakChars, true) !== false) { + if ($this->debug) { + echo 'BREAKCHAR (' . $substr . ') PARSED' . "\n"; + } + $type = self::T_BREAKCHAR; + //$substr = '"'.$substr.'"'; + } else { + $substr = $this->replaceStringVars('\'' . str_replace('\'', '\\\'', $substr) . '\'', '\'', $curBlock); + $type = self::T_UNQUOTED_STRING; + if ($this->debug) { + echo 'BLABBER (' . $substr . ') CASTED AS STRING' . "\n"; + } + } + + if (is_array($parsingParams)) { + $parsingParams[] = array($substr, $src, $type); + + return $parsingParams; + } elseif ($curBlock === 'namedparam') { + return array($substr, $src, $type); + } elseif ($curBlock === 'expression') { + return $substr; + } else { + throw new Exception('Something went wrong'); + } + } + + /** + * Replaces variables within a parsed string. + * + * @param string $string the parsed string + * @param string $first the first character parsed in the string, which is the string delimiter (' or ") + * @param string $curBlock the current parser-block being processed + * + * @return string the original string with variables replaced + */ + protected function replaceStringVars($string, $first, $curBlock = '') + { + $pos = 0; + if ($this->debug) { + echo 'STRING VAR REPLACEMENT : ' . $string . "\n"; + } + // replace vars + while (($pos = strpos($string, '$', $pos)) !== false) { + $prev = substr($string, $pos - 1, 1); + if ($prev === '\\') { + ++ $pos; + continue; + } + + $var = $this->parse($string, $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string'))); + $len = $var[0]; + $var = $this->parse(str_replace('\\' . $first, $first, $string), $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string'))); + + if ($prev === '`' && substr($string, $pos + $len, 1) === '`') { + $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos - 1, $len + 2); + } else { + $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos, $len); + } + $pos += strlen($var[1]) + 2; + if ($this->debug) { + echo 'STRING VAR REPLACEMENT DONE : ' . $string . "\n"; + } + } + + // handle modifiers + // TODO Obsolete? + $string = preg_replace_callback( + '#("|\')\.(.+?)\.\1((?:\|(?:@?[a-z0-9_]+(?:(?::("|\').+?\4|:[^`]*))*))+)#i', array( + $this, + 'replaceModifiers' + ), $string + ); + + // replace escaped dollar operators by unescaped ones if required + if ($first === "'") { + $string = str_replace('\\$', '$', $string); + } + + return $string; + } + + /** + * Replaces the modifiers applied to a string or a variable. + * + * @param array $m the regex matches that must be array(1=>"double or single quotes enclosing a string, + * when applicable", 2=>"the string or var", 3=>"the modifiers matched") + * @param string $curBlock the current parser-block being processed + * @param null $pointer + * + * @return string the input enclosed with various function calls according to the modifiers found + * @throws CompilationException + * @throws Exception + */ + protected function replaceModifiers(array $m, $curBlock = null, &$pointer = null) + { + if ($this->debug) { + echo 'PARSING MODIFIERS : ' . $m[3] . "\n"; + } + + if ($pointer !== null) { + $pointer += strlen($m[3]); + } + // remove first pipe + $cmdstrsrc = substr($m[3], 1); + // remove last quote if present + if (substr($cmdstrsrc, - 1, 1) === $m[1]) { + $cmdstrsrc = substr($cmdstrsrc, 0, - 1); + $add = $m[1]; + } + + $output = $m[2]; + + $continue = true; + while (strlen($cmdstrsrc) > 0 && $continue) { + if ($cmdstrsrc[0] === '|') { + $cmdstrsrc = substr($cmdstrsrc, 1); + continue; + } + if ($cmdstrsrc[0] === ' ' || $cmdstrsrc[0] === ';' || substr($cmdstrsrc, 0, strlen($this->rd)) === $this->rd) { + if ($this->debug) { + echo 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND' . "\n"; + } + $continue = false; + if ($pointer !== null) { + $pointer -= strlen($cmdstrsrc); + } + break; + } + $cmdstr = $cmdstrsrc; + $paramsep = ':'; + if (!preg_match('/^(@{0,2}[a-z_][a-z0-9_]*)(:)?/i', $cmdstr, $match)) { + throw new CompilationException($this, 'Invalid modifier name, started with : ' . substr($cmdstr, 0, 10)); + } + $paramspos = !empty($match[2]) ? strlen($match[1]) : false; + $func = $match[1]; + + $state = 0; + if ($paramspos === false) { + $cmdstrsrc = substr($cmdstrsrc, strlen($func)); + $params = array(); + if ($this->debug) { + echo 'MODIFIER (' . $func . ') CALLED WITH NO PARAMS' . "\n"; + } + } else { + $paramstr = substr($cmdstr, $paramspos + 1); + if (substr($paramstr, - 1, 1) === $paramsep) { + $paramstr = substr($paramstr, 0, - 1); + } + + $ptr = 0; + $params = array(); + while ($ptr < strlen($paramstr)) { + if ($this->debug) { + echo 'MODIFIER (' . $func . ') START PARAM PARSING WITH POINTER AT ' . $ptr . "\n"; + } + if ($this->debug) { + echo $paramstr . '--' . $ptr . '--' . strlen($paramstr) . '--modifier' . "\n"; + } + $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'modifier', $ptr); + if ($this->debug) { + echo 'PARAM PARSED, POINTER AT ' . $ptr . "\n"; + } + + if ($ptr >= strlen($paramstr)) { + if ($this->debug) { + echo 'PARAM PARSING ENDED, PARAM STRING CONSUMED' . "\n"; + } + break; + } + + if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === '|' || $paramstr[$ptr] === ';' || substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) { + if ($this->debug) { + echo 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT ' . $ptr . "\n"; + } + if ($paramstr[$ptr] !== '|') { + $continue = false; + if ($pointer !== null) { + $pointer -= strlen($paramstr) - $ptr; + } + } + ++ $ptr; + break; + } + if ($ptr < strlen($paramstr) && $paramstr[$ptr] === ':') { + ++ $ptr; + } + } + $cmdstrsrc = substr($cmdstrsrc, strlen($func) + 1 + $ptr); + foreach ($params as $k => $p) { + if (is_array($p) && is_array($p[1])) { + $state |= 2; + } else { + if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m)) { + $params[$k] = array($m[2], array('true', 'true')); + } else { + if ($state & 2) { + throw new CompilationException($this, 'You can not use an unnamed parameter after a named one'); + } + $state |= 1; + } + } + } + } + + // check if we must use array_map with this plugin or not + $mapped = false; + if (substr($func, 0, 1) === '@') { + $func = substr($func, 1); + $mapped = true; + } + + $pluginType = $this->getPluginType($func); + + if ($state & 2) { + array_unshift($params, array('value', is_array($output) ? $output : array($output, $output))); + } else { + array_unshift($params, is_array($output) ? $output : array($output, $output)); + } + + if ($pluginType & Core::NATIVE_PLUGIN) { + $params = $this->mapParams($params, null, $state); + + $params = $params['*'][0]; + + $params = self::implode_r($params); + + if ($mapped) { + $output = '$this->arrayMap(\'' . $func . '\', array(' . $params . '))'; + } else { + $output = $func . '(' . $params . ')'; + } + } elseif ($pluginType & Core::PROXY_PLUGIN) { + $params = $this->mapParams($params, $this->getCore()->getPluginProxy()->getCallback($func), $state); + foreach ($params as &$p) { + $p = $p[0]; + } + $output = call_user_func(array($this->getCore()->getPluginProxy(), 'getCode'), $func, $params); + } elseif ($pluginType & Core::SMARTY_MODIFIER) { + $params = $this->mapParams($params, null, $state); + $params = $params['*'][0]; + + $params = self::implode_r($params); + + if ($pluginType & Core::CUSTOM_PLUGIN) { + $callback = $this->customPlugins[$func]['callback']; + if (is_array($callback)) { + if (is_object($callback[0])) { + $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->getCustomPlugin(\'' . $func . '\'), \'' . $callback[1] . '\'), array(' . $params . '))'; + } else { + $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))'; + } + } elseif ($mapped) { + $output = '$this->arrayMap(\'' . $callback . '\', array(' . $params . '))'; + } else { + $output = $callback . '(' . $params . ')'; + } + } elseif ($mapped) { + $output = '$this->arrayMap(\'smarty_modifier_' . $func . '\', array(' . $params . '))'; + } else { + $output = 'smarty_modifier_' . $func . '(' . $params . ')'; + } + } else { + if ($pluginType & Core::CUSTOM_PLUGIN) { + $pluginName = $callback = $this->customPlugins[$func]['callback']; + if (($pluginType & Core::CLASS_PLUGIN) && !is_array($callback)) { + $pluginName = $this->customPlugins[$func]['callback']; + $callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'); + } + } else { + if (class_exists('Plugin' . Core::toCamelCase($func)) !== false || function_exists('Plugin' . + Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '')) + !== false) { + $pluginName = 'Plugin' . Core::toCamelCase($func); + } else { + $pluginName = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func); + } + if ($pluginType & Core::CLASS_PLUGIN) { + $callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'); + } else { + $callback = $pluginName . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''); + } + } + $params = $this->mapParams($params, $callback, $state); + + foreach ($params as &$p) { + $p = $p[0]; + } + + // Only for PHP function, who is not a PHP class + if ($pluginType & Core::FUNC_PLUGIN && !($pluginType & Core::CLASS_PLUGIN)) { + if ($pluginType & Core::COMPILABLE_PLUGIN) { + if ($mapped) { + throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.'); + } + if ($pluginType & Core::CUSTOM_PLUGIN) { + $funcCompiler = $this->customPlugins[$func]['callback']; + } else { + if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) { + $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile'; + } else { + $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . + 'Compile'; + } + } + array_unshift($params, $this); + $output = call_user_func_array($funcCompiler, $params); + } else { + array_unshift($params, '$this'); + + $params = self::implode_r($params); + if ($mapped) { + $output = '$this->arrayMap(\'' . $pluginName . '\', array(' . $params . '))'; + } else { + $output = $pluginName . '(' . $params . ')'; + } + } + } else { + if ($pluginType & Core::COMPILABLE_PLUGIN) { + if ($mapped) { + throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.'); + } + if ($pluginType & Core::CUSTOM_PLUGIN) { + $callback = $this->customPlugins[$func]['callback']; + if (!is_array($callback)) { + if (!method_exists($callback, 'compile')) { + throw new Exception('Custom plugin ' . $func . ' must implement the "compile" method to be compilable, or you should provide a full callback to the method to use'); + } + if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) { + $funcCompiler = array($callback, 'compile'); + } else { + $funcCompiler = array(new $callback(), 'compile'); + } + } else { + $funcCompiler = $callback; + } + } else { + if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) { + $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile'); + } else { + $funcCompiler = array(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func), 'compile'); + } + array_unshift($params, $this); + } + $output = call_user_func_array($funcCompiler, $params); + } else { + $params = self::implode_r($params); + + if ($pluginType & Core::CUSTOM_PLUGIN) { + if (is_object($callback[0])) { + if (is_array($this->getCore()->getCustomPlugin($func))) { + $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))'; + } else { + $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->getCustomPlugin(\'' . $func . '\'), \'' . $callback[1] . '\'), array(' . $params . '))'; + } + } else { + $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))'; + } + } elseif ($mapped) { + $output = '$this->arrayMap(array($this->getObjectPlugin(\''. + Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '\'), + \'process\'), array(' . $params . '))'; + } else { + if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) { + $output = '$this->classCall(\'Plugin' . Core::toCamelCase($func) . '\', array(' . $params . '))'; + } elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($func)) !== false) { + $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . $func . '\', array(' . $params . '))'; + } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) { + $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', array(' . $params . '))'; + } else { + $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))'; + } + } + } + } + } + } + + if ($curBlock === 'namedparam') { + return array($output, $output); + } elseif ($curBlock === 'var' || $m[1] === null) { + return $output; + } elseif ($curBlock === 'string' || $curBlock === 'root') { + return $m[1] . '.' . $output . '.' . $m[1] . (isset($add) ? $add : null); + } + + return ''; + } + + /** + * Recursively implodes an array in a similar manner as var_export() does but with some tweaks + * to handle pre-compiled values and the fact that we do not need to enclose everything with + * "array" and do not require top-level keys to be displayed. + * + * @param array $params the array to implode + * @param bool $recursiveCall if set to true, the function outputs key names for the top level + * + * @return string the imploded array + */ + public static function implode_r(array $params, $recursiveCall = false) + { + $out = ''; + foreach ($params as $k => $p) { + if (is_array($p)) { + $out2 = 'array('; + foreach ($p as $k2 => $v) { + $out2 .= var_export($k2, true) . ' => ' . (is_array($v) ? 'array(' . self::implode_r($v, true) . ')' : $v) . ', '; + } + $p = rtrim($out2, ', ') . ')'; + } + if ($recursiveCall) { + $out .= var_export($k, true) . ' => ' . $p . ', '; + } else { + $out .= $p . ', '; + } + } + + return rtrim($out, ', '); + } + + /** + * Returns the plugin type of a plugin and adds it to the used plugins array if required. + * + * @param string $name plugin name, as found in the template + * + * @return int type as a multi bit flag composed of the Dwoo plugin types constants + * @throws Exception + * @throws SecurityException + * @throws Exception + */ + protected function getPluginType($name) + { + $pluginType = - 1; + + if (($this->securityPolicy === null && (function_exists($name) || strtolower($name) === 'isset' || strtolower($name) === 'empty')) || ($this->securityPolicy !== null && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) !== false)) { + $phpFunc = true; + } elseif ($this->securityPolicy !== null && function_exists($name) && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) === false) { + throw new SecurityException('Call to a disallowed php function : ' . $name); + } + + while ($pluginType <= 0) { + // Template plugin compilable + if (isset($this->templatePlugins[$name])) { + $pluginType = Core::TEMPLATE_PLUGIN | Core::COMPILABLE_PLUGIN; + } // Custom plugin + elseif (isset($this->customPlugins[$name])) { + $pluginType = $this->customPlugins[$name]['type'] | Core::CUSTOM_PLUGIN; + } // Class blocks plugin + elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name)) !== false) { + $pluginType = Core::CLASS_PLUGIN; + if (is_subclass_of(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name), 'Dwoo\Block\Plugin')) { + $pluginType += Core::BLOCK_PLUGIN; + } + $interfaces = class_implements(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name)); + if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) { + $pluginType |= Core::COMPILABLE_PLUGIN; + } + } // Class functions plugin + elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name)) !== false) { + $pluginType = Core::FUNC_PLUGIN + Core::CLASS_PLUGIN; + $interfaces = class_implements(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name)); + if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) { + $pluginType |= Core::COMPILABLE_PLUGIN; + } + } // Class without namespace + elseif (class_exists('Plugin' . Core::toCamelCase($name)) !== false) { + $pluginType = Core::CLASS_PLUGIN; + if (is_subclass_of('Plugin' . Core::toCamelCase($name), 'Dwoo\Block\Plugin')) { + $pluginType += Core::BLOCK_PLUGIN; + } + $interfaces = class_implements('Plugin' . Core::toCamelCase($name)); + if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) { + $pluginType |= Core::COMPILABLE_PLUGIN; + } + } // Function plugin (with/without namespaces) + elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase ($name)) !== + false || function_exists('Plugin' . Core::toCamelCase($name)) !== false) { + $pluginType = Core::FUNC_PLUGIN; + } // Function plugin compile (with/without namespaces) + elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name) . + 'Compile') !== false || function_exists('Plugin' . Core::toCamelCase($name) . 'Compile') !== + false) { + $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN; + } // Helper plugin class compile + elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name)) !== false) { + $pluginType = Core::CLASS_PLUGIN | Core::COMPILABLE_PLUGIN; + } // Helper plugin function compile + elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name) . 'Compile') !== false) { + $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN; + } // Smarty modifier + elseif (function_exists('smarty_modifier_' . $name) !== false) { + $pluginType = Core::SMARTY_MODIFIER; + } // Smarty function + elseif (function_exists('smarty_function_' . $name) !== false) { + $pluginType = Core::SMARTY_FUNCTION; + } // Smarty block + elseif (function_exists('smarty_block_' . $name) !== false) { + $pluginType = Core::SMARTY_BLOCK; + } // Everything else + else { + if ($pluginType === - 1) { + try { + $this->getCore()->getLoader()->loadPlugin('Plugin' . Core::toCamelCase($name)); + } + catch (Exception $e) { + if (isset($phpFunc)) { + $pluginType = Core::NATIVE_PLUGIN; + } elseif (is_object($this->getCore()->getPluginProxy()) && $this->getCore()->getPluginProxy()->handles($name)) { + $pluginType = Core::PROXY_PLUGIN; + break; + } else { + throw $e; + } + } + } else { + throw new Exception('Plugin "' . $name . '" could not be found, type:' . $pluginType); + } + ++ $pluginType; + } + } + + if (($pluginType & Core::COMPILABLE_PLUGIN) === 0 && ($pluginType & Core::NATIVE_PLUGIN) === 0 && ($pluginType & Core::PROXY_PLUGIN) === 0) { + $this->addUsedPlugin(Core::toCamelCase($name), $pluginType); + } + + return $pluginType; + } + + /** + * Allows a plugin to load another one at compile time, this will also mark + * it as used by this template so it will be loaded at runtime (which can be + * useful for compiled plugins that rely on another plugin when their compiled + * code runs). + * + * @param string $name the plugin name + * + * @return void + */ + public function loadPlugin($name) + { + $this->getPluginType($name); + } + + /** + * Runs htmlentities over the matched blocks when the security policy enforces that. + * + * @param array $match matched php block + * + * @return string the htmlentities-converted string + */ + protected function phpTagEncodingHelper($match) + { + return htmlspecialchars($match[0]); + } + + /** + * Maps the parameters received from the template onto the parameters required by the given callback. + * + * @param array $params the array of parameters + * @param callback $callback the function or method to reflect on to find out the required parameters + * @param int $callType the type of call in the template, 0 = no params, 1 = php-style call, 2 = named + * parameters call + * @param array $map the parameter map to use, if not provided it will be built from the callback + * + * @return array parameters sorted in the correct order with missing optional parameters filled + * @throws CompilationException + */ + protected function mapParams(array $params, $callback, $callType = 2, $map = null) + { + if (!$map) { + $map = $this->getParamMap($callback); + } + + $paramlist = array(); + + // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values)) + $ps = array(); + foreach ($params as $p) { + if (is_array($p[1])) { + $ps[$p[0]] = $p[1]; + } else { + $ps[] = $p; + } + } + + // loops over the param map and assigns values from the template or default value for unset optional params + foreach ($map as $k => $v){ + if ($v[0] === '*') { + // "rest" array parameter, fill every remaining params in it and then break + if (count($ps) === 0) { + if ($v[1] === false) { + throw new CompilationException( + $this, 'Rest argument missing for ' . str_replace( + array( + Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin', + 'Compile' + ), '', (is_array($callback) ? $callback[0] : $callback) + ) + ); + } else { + break; + } + } + $tmp = array(); + $tmp2 = array(); + $tmp3 = array(); + foreach ($ps as $i => $p) { + $tmp[$i] = $p[0]; + $tmp2[$i] = $p[1]; + $tmp3[$i] = isset($p[2]) ? $p[2] : 0; + unset($ps[$i]); + } + $paramlist[$v[0]] = array($tmp, $tmp2, $tmp3); + unset($tmp, $tmp2, $i, $p); + break; + } elseif (isset($ps[$v[0]])) { + // parameter is defined as named param + $paramlist[$v[0]] = $ps[$v[0]]; + unset($ps[$v[0]]); + } elseif (isset($ps[$k])) { + // parameter is defined as ordered param + $paramlist[$v[0]] = $ps[$k]; + unset($ps[$k]); + } elseif ($v[1] === false) { + // parameter is not defined and not optional, throw error + if (is_array($callback)) { + if (is_object($callback[0])) { + $name = get_class($callback[0]) . '::' . $callback[1]; + } else { + $name = $callback[0]; + } + } else { + $name = $callback; + } + + throw new CompilationException( + $this, 'Argument ' . $k . '/' . $v[0] . ' missing for ' . str_replace( + array( + Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin', + 'Compile' + ), '', $name + ) + ); + } elseif ($v[2] === null) { + // enforce lowercased null if default value is null (php outputs NULL with var export) + $paramlist[$v[0]] = array('null', null, self::T_NULL); + } else { + // outputs default value with var_export + $paramlist[$v[0]] = array(var_export($v[2], true), $v[2]); + } + } + + if (count($ps)) { + foreach ($ps as $i => $p) { + array_push($paramlist, $p); + } + } + + return $paramlist; + } + + /** + * Returns the parameter map of the given callback, it filters out entries typed as Dwoo and Compiler and turns the + * rest parameter into a "*". + * + * @param callback $callback the function/method to reflect on + * + * @return array processed parameter map + */ + protected function getParamMap($callback) + { + if (is_null($callback)) { + return array(array('*', true)); + } + if (is_array($callback)) { + $ref = new ReflectionMethod($callback[0], $callback[1]); + } else { + $ref = new ReflectionFunction($callback); + } + + $out = array(); + foreach ($ref->getParameters() as $param) { + if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Core') { + continue; + } + if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Compiler') { + continue; + } + if ($param->getName() === 'rest' && $param->isArray() === true) { + $out[] = array('*', $param->isOptional(), null); + continue; + } + $out[] = array( + $param->getName(), + $param->isOptional(), + $param->isOptional() ? $param->getDefaultValue() : null + ); + } + + return $out; + } + + /** + * Returns a default instance of this compiler, used by default by all Dwoo templates that do not have a + * specific compiler assigned and when you do not override the default compiler factory function. + * + * @see Core::setDefaultCompilerFactory() + * @return Compiler + */ + public static function compilerFactory() + { + if (self::$instance === null) { + self::$instance = new self(); + } + + return self::$instance; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Core.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Core.php new file mode 100644 index 0000000..26a6a38 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Core.php @@ -0,0 +1,1795 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE LGPLv3 + * @version 1.3.6 + * @date 2017-03-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +use ArrayAccess; +use Closure; +use Countable; +use Dwoo\Plugins\Blocks\PluginDynamic; +use Dwoo\Security\Policy as SecurityPolicy; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\Template\File as TemplateFile; +use Iterator; +use stdClass; +use Traversable; + +/** + * Main dwoo class, allows communication between the compiler, template and data classes. + *
    + * requirements :
    + *  php 5.3.0 or above (might work below, it's a rough estimate)
    + *  SPL and PCRE extensions (for php versions prior to 5.3.0)
    + *  mbstring extension for some string manipulation plugins (especially if you intend to use UTF-8)
    + * recommended :
    + *  hash extension (for Dwoo\Template\Str - minor performance boost)
    + * project created :
    + *  2008-01-05
    + * 
    + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class Core +{ + /** + * Current version number. + * + * @var string + */ + const VERSION = '1.3.6'; + + /** + * Unique number of this dwoo release, based on version number. + * this can be used by templates classes to check whether the compiled template + * has been compiled before this release or not, so that old templates are + * recompiled automatically when Dwoo is updated + */ + const RELEASE_TAG = 136; + + /** + * Constants that represents all plugin types + * these are bitwise-operation-safe values to allow multiple types + * on a single plugin + * + * @var int + */ + const CLASS_PLUGIN = 1; + const FUNC_PLUGIN = 2; + const NATIVE_PLUGIN = 4; + const BLOCK_PLUGIN = 8; + const COMPILABLE_PLUGIN = 16; + const CUSTOM_PLUGIN = 32; + const SMARTY_MODIFIER = 64; + const SMARTY_BLOCK = 128; + const SMARTY_FUNCTION = 256; + const PROXY_PLUGIN = 512; + const TEMPLATE_PLUGIN = 1024; + + /** + * Constant to default namespaces of builtin plugins + * + * @var string + */ + const NAMESPACE_PLUGINS_BLOCKS = 'Dwoo\Plugins\Blocks\\'; + const NAMESPACE_PLUGINS_FILTERS = 'Dwoo\Plugins\Filters\\'; + const NAMESPACE_PLUGINS_FUNCTIONS = 'Dwoo\Plugins\Functions\\'; + const NAMESPACE_PLUGINS_HELPERS = 'Dwoo\Plugins\Helpers\\'; + const NAMESPACE_PLUGINS_PROCESSORS = 'Dwoo\Plugins\Processors\\'; + + /** + * Character set of the template, used by string manipulation plugins. + * it must be lowercase, but setCharset() will take care of that + * + * @see setCharset + * @see getCharset + * @var string + */ + protected $charset = 'UTF-8'; + + /** + * Global variables that are accessible through $dwoo.* in the templates. + * default values include: + * $dwoo.version - current version number + * $dwoo.ad - a Powered by Dwoo link pointing to dwoo.org + * $dwoo.now - the current time + * $dwoo.template - the current template filename + * $dwoo.charset - the character set used by the template + * on top of that, foreach and other plugins can store special values in there, + * see their documentation for more details. + * + * @var array + */ + protected $globals = array(); + + /** + * Directory where the compiled templates are stored. + * defaults to DWOO_COMPILEDIR (= dwoo_dir/compiled by default) + * + * @var string + */ + protected $compileDir; + + /** + * Directory where the cached templates are stored. + * defaults to DWOO_CACHEDIR (= dwoo_dir/cache by default) + * + * @var string + */ + protected $cacheDir; + + /** + * Directory where the template files are stored + * + * @var array + */ + protected $templateDir = array(); + + /** + * Defines how long (in seconds) the cached files must remain valid. + * can be overridden on a per-template basis + * -1 = never delete + * 0 = disabled + * >0 = duration in seconds + * + * @var int + */ + protected $cacheTime = 0; + + /** + * Security policy object. + * + * @var SecurityPolicy + */ + protected $securityPolicy = null; + + /** + * Stores the custom plugins callbacks. + * + * @see addPlugin + * @see removePlugin + * @var array + */ + protected $plugins = array(); + + /** + * Stores the filter callbacks. + * + * @see addFilter + * @see removeFilter + * @var array + */ + protected $filters = array(); + + /** + * Stores the resource types and associated + * classes / compiler classes. + * + * @var array + */ + protected $resources = array( + 'file' => array( + 'class' => 'Dwoo\Template\File', + 'compiler' => null, + ), + 'string' => array( + 'class' => 'Dwoo\Template\Str', + 'compiler' => null, + ), + ); + + /** + * The dwoo loader object used to load plugins by this dwoo instance. + * + * @var ILoader + */ + protected $loader = null; + + /** + * Currently rendered template, set to null when not-rendering. + * + * @var ITemplate + */ + protected $template = null; + + /** + * Stores the instances of the class plugins during template runtime. + * + * @var array + */ + protected $runtimePlugins = array(); + + /** + * Stores the returned values during template runtime. + * + * @var array + */ + protected $returnData = array(); + + /** + * Stores the data during template runtime. + * + * @var array + */ + protected $data = array(); + + /** + * Stores the current scope during template runtime. + * this should ideally not be accessed directly from outside template code + * + * @var mixed + */ + public $scope; + + /** + * Stores the scope tree during template runtime. + * + * @var array + */ + protected $scopeTree = array(); + + /** + * Stores the block plugins stack during template runtime. + * + * @var array + */ + protected $stack = array(); + + /** + * Stores the current block plugin at the top of the stack during template runtime. + * + * @var BlockPlugin + */ + protected $curBlock; + + /** + * Stores the output buffer during template runtime. + * + * @var string + */ + protected $buffer; + + /** + * Stores plugin proxy. + * + * @var IPluginProxy + */ + protected $pluginProxy; + + /** + * Constructor, sets the cache and compile dir to the default values if not provided. + * + * @param string $compileDir path to the compiled directory, defaults to lib/compiled + * @param string $cacheDir path to the cache directory, defaults to lib/cache + */ + public function __construct($compileDir = null, $cacheDir = null) + { + if ($compileDir !== null) { + $this->setCompileDir($compileDir); + } + if ($cacheDir !== null) { + $this->setCacheDir($cacheDir); + } + $this->initGlobals(); + } + + /** + * Resets some runtime variables to allow a cloned object to be used to render sub-templates. + * + * @return void + */ + public function __clone() + { + $this->template = null; + unset($this->data); + unset($this->returnData); + } + + /** + * Returns the given template rendered using the provided data and optional compiler. + * + * @param mixed $_tpl template, can either be a ITemplate object (i.e. TemplateFile), a + * valid path to a template, or a template as a string it is recommended to + * provide a ITemplate as it will probably make things faster, especially if + * you render a template multiple times + * @param mixed $data the data to use, can either be a IDataProvider object (i.e. Data) or + * an associative array. if you're rendering the template from cache, it can be + * left null + * @param ICompiler $_compiler the compiler that must be used to compile the template, if left empty a default + * Compiler will be used + * + * @return string|void or the template output if $output is false + * @throws Exception + */ + public function get($_tpl, $data = array(), $_compiler = null) + { + // a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one + if ($this->template instanceof ITemplate) { + $clone = clone $this; + + return $clone->get($_tpl, $data, $_compiler); + } + + // auto-create template if required + if ($_tpl instanceof ITemplate) { + // valid, skip + } elseif (is_string($_tpl)) { + $_tpl = new TemplateFile($_tpl); + $_tpl->setIncludePath($this->getTemplateDir()); + } else { + throw new Exception('Dwoo->get\'s first argument must be a ITemplate (i.e. TemplateFile) or a valid path to a template file', E_USER_NOTICE); + } + + // save the current template, enters render mode at the same time + // if another rendering is requested it will be proxied to a new Core(instance + $this->template = $_tpl; + + // load data + if ($data instanceof IDataProvider) { + $this->data = $data->getData(); + } elseif (is_array($data)) { + $this->data = $data; + } elseif ($data instanceof ArrayAccess) { + $this->data = $data; + } else { + throw new Exception('Dwoo->get/Dwoo->output\'s data argument must be a IDataProvider object (i.e. Data) or an associative array', E_USER_NOTICE); + } + + $this->addGlobal('template', $_tpl->getName()); + $this->initRuntimeVars($_tpl); + + // try to get cached template + $file = $_tpl->getCachedTemplate($this); + $doCache = $file === true; + $cacheLoaded = is_string($file); + + if ($cacheLoaded === true) { + // cache is present, run it + ob_start(); + include $file; + $this->template = null; + + return ob_get_clean(); + } else { + $dynamicId = uniqid(); + + // render template + $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler); + $out = include $compiledTemplate; + + // template returned false so it needs to be recompiled + if ($out === false) { + $_tpl->forceCompilation(); + $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler); + $out = include $compiledTemplate; + } + + if ($doCache === true) { + $out = preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '', $out); + if (!class_exists(self::NAMESPACE_PLUGINS_BLOCKS . 'PluginDynamic')) { + $this->getLoader()->loadPlugin('PluginDynamic'); + } + $out = PluginDynamic::unescape($out, $dynamicId, $compiledTemplate); + } + + // process filters + foreach ($this->filters as $filter) { + if (is_array($filter) && $filter[0] instanceof Filter) { + $out = call_user_func($filter, $out); + } else { + $out = call_user_func($filter, $this, $out); + } + } + + if ($doCache === true) { + // building cache + $file = $_tpl->cache($this, $out); + + // run it from the cache to be sure dynamics are rendered + ob_start(); + include $file; + // exit render mode + $this->template = null; + + return ob_get_clean(); + } else { + // no need to build cache + // exit render mode + $this->template = null; + + return $out; + } + } + } + + /** + * Registers a Global. + * New globals can be added before compiling or rendering a template + * but after, you can only update existing globals. + * + * @param string $name + * @param mixed $value + * + * @return $this + * @throws Exception + */ + public function addGlobal($name, $value) + { + if (null === $this->globals) { + $this->initGlobals(); + } + + $this->globals[$name] = $value; + + return $this; + } + + /** + * Gets the registered Globals. + * + * @return array + */ + public function getGlobals() + { + return $this->globals; + } + + /** + * Re-initializes the globals array before each template run. + * this method is only callede once when the Dwoo object is created + * + * @return void + */ + protected function initGlobals() + { + $this->globals = array( + 'version' => self::VERSION, + 'ad' => '
    Powered by Dwoo', + 'now' => $_SERVER['REQUEST_TIME'], + 'charset' => $this->getCharset(), + ); + } + + /** + * Re-initializes the runtime variables before each template run. + * override this method to inject data in the globals array if needed, this + * method is called before each template execution + * + * @param ITemplate $tpl the template that is going to be rendered + * + * @return void + */ + protected function initRuntimeVars(ITemplate $tpl) + { + $this->runtimePlugins = array(); + $this->scope = &$this->data; + $this->scopeTree = array(); + $this->stack = array(); + $this->curBlock = null; + $this->buffer = ''; + $this->returnData = array(); + } + + /** + * Adds a custom plugin that is not in one of the plugin directories. + * + * @param string $name the plugin name to be used in the templates + * @param callback $callback the plugin callback, either a function name, + * a class name or an array containing an object + * or class name and a method name + * @param bool $compilable if set to true, the plugin is assumed to be compilable + * + * @return void + * @throws Exception + */ + public function addPlugin($name, $callback, $compilable = false) + { + $compilable = $compilable ? self::COMPILABLE_PLUGIN : 0; + if (is_array($callback)) { + if (is_subclass_of(is_object($callback[0]) ? get_class($callback[0]) : $callback[0], 'Dwoo\Block\Plugin')) { + $this->plugins[$name] = array( + 'type' => self::BLOCK_PLUGIN | $compilable, + 'callback' => $callback, + 'class' => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]) + ); + } else { + $this->plugins[$name] = array( + 'type' => self::CLASS_PLUGIN | $compilable, + 'callback' => $callback, + 'class' => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]), + 'function' => $callback[1] + ); + } + } elseif (is_string($callback)) { + if (class_exists($callback)) { + if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) { + $this->plugins[$name] = array( + 'type' => self::BLOCK_PLUGIN | $compilable, + 'callback' => $callback, + 'class' => $callback + ); + } else { + $this->plugins[$name] = array( + 'type' => self::CLASS_PLUGIN | $compilable, + 'callback' => $callback, + 'class' => $callback, + 'function' => ($compilable ? 'compile' : 'process') + ); + } + } elseif (function_exists($callback)) { + $this->plugins[$name] = array( + 'type' => self::FUNC_PLUGIN | $compilable, + 'callback' => $callback + ); + } else { + throw new Exception('Callback could not be processed correctly, please check that the function/class you used exists'); + } + } elseif ($callback instanceof Closure) { + $this->plugins[$name] = array( + 'type' => self::FUNC_PLUGIN | $compilable, + 'callback' => $callback + ); + } elseif (is_object($callback)) { + if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) { + $this->plugins[$name] = array( + 'type' => self::BLOCK_PLUGIN | $compilable, + 'callback' => get_class($callback), + 'class' => $callback + ); + } else { + $this->plugins[$name] = array( + 'type' => self::CLASS_PLUGIN | $compilable, + 'callback' => $callback, + 'class' => $callback, + 'function' => ($compilable ? 'compile' : 'process') + ); + } + } else { + throw new Exception('Callback could not be processed correctly, please check that the function/class you used exists'); + } + } + + /** + * Removes a custom plugin. + * + * @param string $name the plugin name + * + * @return void + */ + public function removePlugin($name) + { + if (isset($this->plugins[$name])) { + unset($this->plugins[$name]); + } + } + + /** + * Adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this + * instance. + * + * @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory + * @param bool $autoload if true, the first parameter must be a filter name from one of the plugin directories + * + * @return void + * @throws Exception + */ + public function addFilter($callback, $autoload = false) + { + if ($autoload) { + $class = self::NAMESPACE_PLUGINS_FILTERS . self::toCamelCase($callback); + if (!class_exists($class) && !function_exists($class)) { + try { + $this->getLoader()->loadPlugin($callback); + } + catch (Exception $e) { + if (strstr($callback, self::NAMESPACE_PLUGINS_FILTERS)) { + throw new Exception('Wrong filter name : ' . $callback . ', the "Filter" prefix should not be used, please only use "' . str_replace('Filter', '', $callback) . '"'); + } else { + throw new Exception('Wrong filter name : ' . $callback . ', when using autoload the filter must be in one of your plugin dir as "name.php" containig a class or function named "Filter"'); + } + } + } + + if (class_exists($class)) { + $callback = array(new $class($this), 'process'); + } elseif (function_exists($class)) { + $callback = $class; + } else { + throw new Exception('Wrong filter name : ' . $callback . ', when using autoload the filter must be in one of your plugin dir as "name.php" containig a class or function named "Filter"'); + } + + $this->filters[] = $callback; + } else { + $this->filters[] = $callback; + } + } + + /** + * Removes a filter. + * + * @param mixed $callback callback or filter name if it was autoloaded + * + * @return void + */ + public function removeFilter($callback) + { + if (($index = array_search(self::NAMESPACE_PLUGINS_FILTERS. 'Filter' . self::toCamelCase($callback), $this->filters, + true)) !== + false) { + unset($this->filters[$index]); + } elseif (($index = array_search($callback, $this->filters, true)) !== false) { + unset($this->filters[$index]); + } else { + $class = self::NAMESPACE_PLUGINS_FILTERS . 'Filter' . $callback; + foreach ($this->filters as $index => $filter) { + if (is_array($filter) && $filter[0] instanceof $class) { + unset($this->filters[$index]); + break; + } + } + } + } + + /** + * Adds a resource or overrides a default one. + * + * @param string $name the resource name + * @param string $class the resource class (which must implement ITemplate) + * @param callback $compilerFactory the compiler factory callback, a function that must return a compiler instance + * used to compile this resource, if none is provided. by default it will produce + * a Compiler object + * + * @return void + * @throws Exception + */ + public function addResource($name, $class, $compilerFactory = null) + { + if (strlen($name) < 2) { + throw new Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths'); + } + + if (!class_exists($class)) { + throw new Exception(sprintf('Resource class %s does not exist', $class)); + } + + $interfaces = class_implements($class); + if (in_array('Dwoo\ITemplate', $interfaces) === false) { + throw new Exception('Resource class must implement ITemplate'); + } + + $this->resources[$name] = array( + 'class' => $class, + 'compiler' => $compilerFactory + ); + } + + /** + * Removes a custom resource. + * + * @param string $name the resource name + * + * @return void + */ + public function removeResource($name) + { + unset($this->resources[$name]); + if ($name === 'file') { + $this->resources['file'] = array( + 'class' => 'Dwoo\Template\File', + 'compiler' => null + ); + } + } + + /** + * Sets the loader object to use to load plugins. + * + * @param ILoader $loader loader + * + * @return void + */ + public function setLoader(ILoader $loader) + { + $this->loader = $loader; + } + + /** + * Returns the current loader object or a default one if none is currently found. + * + * @return ILoader|Loader + */ + public function getLoader() + { + if ($this->loader === null) { + $this->loader = new Loader($this->getCompileDir()); + } + + return $this->loader; + } + + /** + * Returns the custom plugins loaded. + * Used by the ITemplate classes to pass the custom plugins to their ICompiler instance. + * + * @return array + */ + public function getCustomPlugins() + { + return $this->plugins; + } + + /** + * Return a specified custom plugin loaded by his name. + * Used by the compiler, for executing a Closure. + * + * @param string $name + * + * @return mixed|null + */ + public function getCustomPlugin($name) + { + if (isset($this->plugins[$name])) { + return $this->plugins[$name]['callback']; + } + + return null; + } + + /** + * Returns the cache directory with a trailing DIRECTORY_SEPARATOR. + * + * @return string + */ + public function getCacheDir() + { + if ($this->cacheDir === null) { + $this->setCacheDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR); + } + + return $this->cacheDir; + } + + /** + * Sets the cache directory and automatically appends a DIRECTORY_SEPARATOR. + * + * @param string $dir the cache directory + * + * @return void + * @throws Exception + */ + public function setCacheDir($dir) + { + $this->cacheDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; + if (!file_exists($this->cacheDir)) { + mkdir($this->cacheDir, 0777, true); + } + if (is_writable($this->cacheDir) === false) { + throw new Exception('The cache directory must be writable, chmod "' . $this->cacheDir . '" to make it writable'); + } + } + + /** + * Returns the compile directory with a trailing DIRECTORY_SEPARATOR. + * + * @return string + */ + public function getCompileDir() + { + if ($this->compileDir === null) { + $this->setCompileDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'compiled' . DIRECTORY_SEPARATOR); + } + + return $this->compileDir; + } + + /** + * Sets the compile directory and automatically appends a DIRECTORY_SEPARATOR. + * + * @param string $dir the compile directory + * + * @return void + * @throws Exception + */ + public function setCompileDir($dir) + { + $this->compileDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; + if (!file_exists($this->compileDir)) { + mkdir($this->compileDir, 0777, true); + } + if (is_writable($this->compileDir) === false) { + throw new Exception('The compile directory must be writable, chmod "' . $this->compileDir . '" to make it writable'); + } + } + + /** + * Returns an array of the template directory with a trailing DIRECTORY_SEPARATOR + * + * @return array + */ + public function getTemplateDir() + { + return $this->templateDir; + } + + /** + * sets the template directory and automatically appends a DIRECTORY_SEPARATOR + * template directory is stored in an array + * + * @param string $dir + * + * @throws Exception + */ + public function setTemplateDir($dir) + { + $tmpDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; + if (is_dir($tmpDir) === false) { + throw new Exception('The template directory: "' . $tmpDir . '" does not exists, create the directory or specify an other location !'); + } + $this->templateDir[] = $tmpDir; + } + + /** + * Returns the default cache time that is used with templates that do not have a cache time set. + * + * @return int the duration in seconds + */ + public function getCacheTime() + { + return $this->cacheTime; + } + + /** + * Sets the default cache time to use with templates that do not have a cache time set. + * + * @param int $seconds the duration in seconds + * + * @return void + */ + public function setCacheTime($seconds) + { + $this->cacheTime = (int)$seconds; + } + + /** + * Returns the character set used by the string manipulation plugins. + * the charset is automatically lowercased + * + * @return string + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Sets the character set used by the string manipulation plugins. + * the charset will be automatically lowercased + * + * @param string $charset the character set + * + * @return void + */ + public function setCharset($charset) + { + $this->charset = strtolower((string)$charset); + } + + /** + * Returns the current template being rendered, when applicable, or null. + * + * @return ITemplate|null + */ + public function getTemplate() + { + return $this->template; + } + + /** + * Sets the current template being rendered. + * + * @param ITemplate $tpl template object + * + * @return void + */ + public function setTemplate(ITemplate $tpl) + { + $this->template = $tpl; + } + + /** + * Sets the default compiler factory function for the given resource name. + * a compiler factory must return a ICompiler object pre-configured to fit your needs + * + * @param string $resourceName the resource name (i.e. file, string) + * @param callback $compilerFactory the compiler factory callback + * + * @return void + */ + public function setDefaultCompilerFactory($resourceName, $compilerFactory) + { + $this->resources[$resourceName]['compiler'] = $compilerFactory; + } + + /** + * Returns the default compiler factory function for the given resource name. + * + * @param string $resourceName the resource name + * + * @return callback the compiler factory callback + */ + public function getDefaultCompilerFactory($resourceName) + { + return $this->resources[$resourceName]['compiler']; + } + + /** + * Sets the security policy object to enforce some php security settings. + * use this if untrusted persons can modify templates + * + * @param SecurityPolicy $policy the security policy object + * + * @return void + */ + public function setSecurityPolicy(SecurityPolicy $policy = null) + { + $this->securityPolicy = $policy; + } + + /** + * Returns the current security policy object or null by default. + * + * @return SecurityPolicy|null the security policy object if any + */ + public function getSecurityPolicy() + { + return $this->securityPolicy; + } + + /** + * Sets the object that must be used as a plugin proxy when plugin can't be found + * by dwoo's loader. + * + * @param IPluginProxy $pluginProxy the proxy object + * + * @return void + */ + public function setPluginProxy(IPluginProxy $pluginProxy) + { + $this->pluginProxy = $pluginProxy; + } + + /** + * Returns the current plugin proxy object or null by default. + * + * @return IPluginProxy + */ + public function getPluginProxy() + { + return $this->pluginProxy; + } + + /** + * Checks whether the given template is cached or not. + * + * @param ITemplate $tpl the template object + * + * @return bool + */ + public function isCached(ITemplate $tpl) + { + return is_string($tpl->getCachedTemplate($this)); + } + + /** + * Clear templates inside the compiled directory. + * + * @return int + */ + public function clearCompiled() + { + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCompileDir()), \RecursiveIteratorIterator::SELF_FIRST); + $count = 0; + foreach ($iterator as $file) { + if ($file->isFile()) { + $count += unlink($file->__toString()) ? 1 : 0; + } + } + + return $count; + } + + /** + * Clears the cached templates if they are older than the given time. + * + * @param int $olderThan minimum time (in seconds) required for a cached template to be cleared + * + * @return int the amount of templates cleared + */ + public function clearCache($olderThan = - 1) + { + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCacheDir()), \RecursiveIteratorIterator::SELF_FIRST); + $expired = time() - $olderThan; + $count = 0; + foreach ($iterator as $file) { + if ($file->isFile() && $file->getCTime() < $expired) { + $count += unlink((string)$file) ? 1 : 0; + } + } + + return $count; + } + + /** + * Fetches a template object of the given resource. + * + * @param string $resourceName the resource name (i.e. file, string) + * @param string $resourceId the resource identifier (i.e. file path) + * @param int $cacheTime the cache time setting for this resource + * @param string $cacheId the unique cache identifier + * @param string $compileId the unique compiler identifier + * @param ITemplate $parentTemplate the parent template + * + * @return ITemplate + * @throws Exception + */ + public function templateFactory($resourceName, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, ITemplate $parentTemplate = null) + { + if (isset($this->resources[$resourceName])) { + /** + * Interface ITemplate + * + * @var ITemplate $class + */ + $class = $this->resources[$resourceName]['class']; + + return $class::templateFactory($this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate); + } + + throw new Exception('Unknown resource type : ' . $resourceName); + } + + /** + * Checks if the input is an array or arrayaccess object, optionally it can also check if it's + * empty. + * + * @param mixed $value the variable to check + * @param bool $checkIsEmpty if true, the function will also check if the array|arrayaccess is empty, + * and return true only if it's not empty + * + * @return int|bool true if it's an array|arrayaccess (or the item count if $checkIsEmpty is true) or false if it's + * not an array|arrayaccess (or 0 if $checkIsEmpty is true) + */ + public function isArray($value, $checkIsEmpty = false) + { + if (is_array($value) === true || $value instanceof ArrayAccess) { + if ($checkIsEmpty === false) { + return true; + } + + return $this->count($value); + } + + return false; + } + + /** + * Checks if the input is an array or a traversable object, optionally it can also check if it's + * empty. + * + * @param mixed $value the variable to check + * @param bool $checkIsEmpty if true, the function will also check if the array|traversable is empty, + * and return true only if it's not empty + * + * @return int|bool true if it's an array|traversable (or the item count if $checkIsEmpty is true) or false if it's + * not an array|traversable (or 0 if $checkIsEmpty is true) + */ + public function isTraversable($value, $checkIsEmpty = false) + { + if (is_array($value) === true) { + if ($checkIsEmpty === false) { + return true; + } else { + return count($value) > 0; + } + } elseif ($value instanceof Traversable) { + if ($checkIsEmpty === false) { + return true; + } else { + return $this->count($value); + } + } + + return false; + } + + /** + * Counts an array or arrayaccess/traversable object. + * + * @param mixed $value the value to count + * + * @return int|bool the count for arrays and objects that implement countable, true for other objects that don't, + * and 0 for empty elements + */ + public function count($value) + { + if (is_array($value) === true || $value instanceof Countable) { + return count($value); + } elseif ($value instanceof ArrayAccess) { + if ($value->offsetExists(0)) { + return true; + } + } elseif ($value instanceof Iterator) { + $value->rewind(); + if ($value->valid()) { + return true; + } + } elseif ($value instanceof Traversable) { + foreach ($value as $dummy) { + return true; + } + } + + return 0; + } + + /** + * Triggers a dwoo error. + * + * @param string $message the error message + * @param int $level the error level, one of the PHP's E_* constants + * + * @return void + */ + public function triggerError($message, $level = E_USER_NOTICE) + { + if (!($tplIdentifier = $this->template->getResourceIdentifier())) { + $tplIdentifier = $this->template->getResourceName(); + } + trigger_error('Dwoo error (in ' . $tplIdentifier . ') : ' . $message, $level); + } + + /** + * Adds a block to the block stack. + * + * @param string $blockName the block name (without `Plugin` prefix) + * @param array $args the arguments to be passed to the block's init() function + * + * @return BlockPlugin the newly created block + */ + public function addStack($blockName, array $args = array()) + { + if (isset($this->plugins[$blockName])) { + $class = $this->plugins[$blockName]['class']; + } else { + $class = current(array_filter([ + 'Plugin' . self::toCamelCase($blockName), + self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . self::toCamelCase($blockName) + ], 'class_exists')); + } + + if ($this->curBlock !== null) { + $this->curBlock->buffer(ob_get_contents()); + ob_clean(); + } else { + $this->buffer .= ob_get_contents(); + ob_clean(); + } + + $block = new $class($this); + + call_user_func_array(array($block, 'init'), $args); + + $this->stack[] = $this->curBlock = $block; + + return $block; + } + + /** + * Removes the plugin at the top of the block stack. + * Calls the block buffer() function, followed by a call to end() and finally a call to process() + * + * @return void + */ + public function delStack() + { + $args = func_get_args(); + + $this->curBlock->buffer(ob_get_contents()); + ob_clean(); + + call_user_func_array(array($this->curBlock, 'end'), $args); + + $tmp = array_pop($this->stack); + + if (count($this->stack) > 0) { + $this->curBlock = end($this->stack); + $this->curBlock->buffer($tmp->process()); + } else { + if ($this->buffer !== '') { + echo $this->buffer; + $this->buffer = ''; + } + $this->curBlock = null; + echo $tmp->process(); + } + + unset($tmp); + } + + /** + * Returns the parent block of the given block. + * + * @param BlockPlugin $block the block class plugin + * + * @return BlockPlugin|false if the given block isn't in the stack + */ + public function getParentBlock(BlockPlugin $block) + { + $index = array_search($block, $this->stack, true); + if ($index !== false && $index > 0) { + return $this->stack[$index - 1]; + } + + return false; + } + + /** + * Finds the closest block of the given type, starting at the top of the stack. + * + * @param string $type the type of plugin you want to find + * + * @return BlockPlugin|false if no plugin of such type is in the stack + */ + public function findBlock($type) + { + if (isset($this->plugins[$type])) { + $type = $this->plugins[$type]['class']; + } else { + $type = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin_' . str_replace(self::NAMESPACE_PLUGINS_BLOCKS.'Plugin', + '', $type); + } + + $keys = array_keys($this->stack); + while (($key = array_pop($keys)) !== false) { + if ($this->stack[$key] instanceof $type) { + return $this->stack[$key]; + } + } + + return false; + } + + /** + * Returns a Plugin of the given class. + * this is so a single instance of every class plugin is created at each template run, + * allowing class plugins to have "per-template-run" static variables + * + * @param string $class the class name + * + * @return mixed an object of the given class + */ + public function getObjectPlugin($class) + { + if (isset($this->runtimePlugins[$class])) { + return $this->runtimePlugins[$class]; + } + + return $this->runtimePlugins[$class] = new $class($this); + } + + /** + * Calls the process() method of the given class-plugin name. + * + * @param string $plugName the class plugin name (without `Plugin` prefix) + * @param array $params an array of parameters to send to the process() method + * + * @return string the process() return value + */ + public function classCall($plugName, array $params = array()) + { + $class = self::toCamelCase($plugName); + $plugin = $this->getObjectPlugin($class); + + return call_user_func_array(array($plugin, 'process'), $params); + } + + /** + * Calls a php function. + * + * @param string $callback the function to call + * @param array $params an array of parameters to send to the function + * + * @return mixed the return value of the called function + */ + public function arrayMap($callback, array $params) + { + if ($params[0] === $this) { + $addThis = true; + array_shift($params); + } + if ((is_array($params[0]) || ($params[0] instanceof Iterator && $params[0] instanceof ArrayAccess))) { + if (empty($params[0])) { + return $params[0]; + } + + // array map + $out = array(); + $cnt = count($params); + + if (isset($addThis)) { + array_unshift($params, $this); + $items = $params[1]; + $keys = array_keys($items); + + if (is_string($callback) === false) { + while (($i = array_shift($keys)) !== null) { + $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params); + } + } elseif ($cnt === 1) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($this, $items[$i]); + } + } elseif ($cnt === 2) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($this, $items[$i], $params[2]); + } + } elseif ($cnt === 3) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($this, $items[$i], $params[2], $params[3]); + } + } else { + while (($i = array_shift($keys)) !== null) { + $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params); + } + } + } else { + $items = $params[0]; + $keys = array_keys($items); + + if (is_string($callback) === false) { + while (($i = array_shift($keys)) !== null) { + $out[] = call_user_func_array($callback, array($items[$i]) + $params); + } + } elseif ($cnt === 1) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($items[$i]); + } + } elseif ($cnt === 2) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($items[$i], $params[1]); + } + } elseif ($cnt === 3) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($items[$i], $params[1], $params[2]); + } + } elseif ($cnt === 4) { + while (($i = array_shift($keys)) !== null) { + $out[] = $callback($items[$i], $params[1], $params[2], $params[3]); + } + } else { + while (($i = array_shift($keys)) !== null) { + $out[] = call_user_func_array($callback, array($items[$i]) + $params); + } + } + } + + return $out; + } else { + return $params[0]; + } + } + + /** + * Reads a variable into the given data array. + * + * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property") + * @param mixed $data the data array or object to read from + * @param bool $safeRead if true, the function will check whether the index exists to prevent any notices from + * being output + * + * @return mixed + */ + public function readVarInto($varstr, $data, $safeRead = false) + { + if ($data === null) { + return null; + } + + if (is_array($varstr) === false) { + preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m); + } else { + $m = $varstr; + } + unset($varstr); + + foreach ($m[1] as $k => $sep) { + if ($sep === '.' || $sep === '[' || $sep === '') { + // strip enclosing quotes if present + $m[2][$k] = preg_replace('#^(["\']?)(.*?)\1$#', '$2', $m[2][$k]); + + if ((is_array($data) || $data instanceof ArrayAccess) && ($safeRead === false || isset($data[$m[2][$k]]))) { + $data = $data[$m[2][$k]]; + } else { + return null; + } + } else { + if (is_object($data) && ($safeRead === false || isset($data->{$m[2][$k]}))) { + $data = $data->{$m[2][$k]}; + } else { + return null; + } + } + } + + return $data; + } + + /** + * Reads a variable into the parent scope. + * + * @param int $parentLevels the amount of parent levels to go from the current scope + * @param string $varstr the variable string, using dwoo variable syntax (i.e. + * "var.subvar[subsubvar]->property") + * + * @return mixed + */ + public function readParentVar($parentLevels, $varstr = null) + { + $tree = $this->scopeTree; + $cur = $this->data; + + while ($parentLevels -- !== 0) { + array_pop($tree); + } + + while (($i = array_shift($tree)) !== null) { + if (is_object($cur)) { + $cur = $cur->{$i}; + } else { + $cur = $cur[$i]; + } + } + + if ($varstr !== null) { + return $this->readVarInto($varstr, $cur); + } else { + return $cur; + } + } + + /** + * Reads a variable into the current scope. + * + * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property") + * + * @return mixed + */ + public function readVar($varstr) + { + if (is_array($varstr) === true) { + $m = $varstr; + unset($varstr); + } else { + if (strstr($varstr, '.') === false && strstr($varstr, '[') === false && strstr($varstr, '->') === false) { + if ($varstr === 'dwoo') { + return $this->getGlobals(); + } elseif ($varstr === '__' || $varstr === '_root') { + return $this->data; + } elseif ($varstr === '_' || $varstr === '_parent') { + $varstr = '.' . $varstr; + $tree = $this->scopeTree; + $cur = $this->data; + array_pop($tree); + + while (($i = array_shift($tree)) !== null) { + if (is_object($cur)) { + $cur = $cur->{$i}; + } else { + $cur = $cur[$i]; + } + } + + return $cur; + } + + $cur = $this->scope; + + if (isset($cur[$varstr])) { + return $cur[$varstr]; + } else { + return null; + } + } + + if (substr($varstr, 0, 1) === '.') { + $varstr = 'dwoo' . $varstr; + } + + preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m); + } + + $i = $m[2][0]; + if ($i === 'dwoo') { + $cur = $this->getGlobals(); + array_shift($m[2]); + array_shift($m[1]); + switch ($m[2][0]) { + case 'get': + $cur = $_GET; + break; + case 'post': + $cur = $_POST; + break; + case 'session': + $cur = $_SESSION; + break; + case 'cookies': + case 'cookie': + $cur = $_COOKIE; + break; + case 'server': + $cur = $_SERVER; + break; + case 'env': + $cur = $_ENV; + break; + case 'request': + $cur = $_REQUEST; + break; + case 'const': + array_shift($m[2]); + if (defined($m[2][0])) { + return constant($m[2][0]); + } else { + return null; + } + } + if ($cur !== $this->getGlobals()) { + array_shift($m[2]); + array_shift($m[1]); + } + } elseif ($i === '__' || $i === '_root') { + $cur = $this->data; + array_shift($m[2]); + array_shift($m[1]); + } elseif ($i === '_' || $i === '_parent') { + $tree = $this->scopeTree; + $cur = $this->data; + + while (true) { + array_pop($tree); + array_shift($m[2]); + array_shift($m[1]); + if (current($m[2]) === '_' || current($m[2]) === '_parent') { + continue; + } + + while (($i = array_shift($tree)) !== null) { + if (is_object($cur)) { + $cur = $cur->{$i}; + } else { + $cur = $cur[$i]; + } + } + break; + } + } else { + $cur = $this->scope; + } + + foreach ($m[1] as $k => $sep) { + if ($sep === '.' || $sep === '[' || $sep === '') { + if ((is_array($cur) || $cur instanceof ArrayAccess) && isset($cur[$m[2][$k]])) { + $cur = $cur[$m[2][$k]]; + } else { + return null; + } + } elseif ($sep === '->') { + if (is_object($cur)) { + $cur = $cur->{$m[2][$k]}; + } else { + return null; + } + } else { + return null; + } + } + + return $cur; + } + + /** + * Assign the value to the given variable. + * + * @param mixed $value the value to assign + * @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property") + * + * @return bool true if assigned correctly or false if a problem occured while parsing the var string + */ + public function assignInScope($value, $scope) + { + if (!is_string($scope)) { + $this->triggerError('Assignments must be done into strings, (' . gettype($scope) . ') ' . var_export($scope, true) . ' given', E_USER_ERROR); + } + if (strstr($scope, '.') === false && strstr($scope, '->') === false) { + $this->scope[$scope] = $value; + } else { + // TODO handle _root/_parent scopes ? + preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $scope, $m); + + $cur = &$this->scope; + $last = array( + array_pop($m[1]), + array_pop($m[2]) + ); + + foreach ($m[1] as $k => $sep) { + if ($sep === '.' || $sep === '[' || $sep === '') { + if (is_array($cur) === false) { + $cur = array(); + } + $cur = &$cur[$m[2][$k]]; + } elseif ($sep === '->') { + if (is_object($cur) === false) { + $cur = new stdClass(); + } + $cur = &$cur->{$m[2][$k]}; + } else { + return false; + } + } + + if ($last[0] === '.' || $last[0] === '[' || $last[0] === '') { + if (is_array($cur) === false) { + $cur = array(); + } + $cur[$last[1]] = $value; + } elseif ($last[0] === '->') { + if (is_object($cur) === false) { + $cur = new stdClass(); + } + $cur->{$last[1]} = $value; + } else { + return false; + } + } + } + + /** + * Sets the scope to the given scope string or array. + * + * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2") + * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope + * + * @return array the current scope tree + */ + public function setScope($scope, $absolute = false) + { + $old = $this->scopeTree; + + if (is_string($scope) === true) { + $scope = explode('.', $scope); + } + + if ($absolute === true) { + $this->scope = &$this->data; + $this->scopeTree = array(); + } + + while (($bit = array_shift($scope)) !== null) { + if ($bit === '_' || $bit === '_parent') { + array_pop($this->scopeTree); + $this->scope = &$this->data; + $cnt = count($this->scopeTree); + for ($i = 0; $i < $cnt; ++ $i) { + $this->scope = &$this->scope[$this->scopeTree[$i]]; + } + } elseif ($bit === '__' || $bit === '_root') { + $this->scope = &$this->data; + $this->scopeTree = array(); + } elseif (isset($this->scope[$bit])) { + if ($this->scope instanceof ArrayAccess) { + $tmp = $this->scope[$bit]; + $this->scope = &$tmp; + } else { + $this->scope = &$this->scope[$bit]; + } + $this->scopeTree[] = $bit; + } else { + unset($this->scope); + $this->scope = null; + } + } + + return $old; + } + + /** + * Returns the entire data array. + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Sets a return value for the currently running template. + * + * @param string $name var name + * @param mixed $value var value + * + * @return void + */ + public function setReturnValue($name, $value) + { + $this->returnData[$name] = $value; + } + + /** + * Retrieves the return values set by the template. + * + * @return array + */ + public function getReturnValues() + { + return $this->returnData; + } + + /** + * Returns a reference to the current scope. + * + * @return mixed + */ + public function &getScope() + { + return $this->scope; + } + + /** + * Redirects all calls to unexisting to plugin proxy. + * + * @param string $method the method name + * @param array $args array of arguments + * + * @return mixed + * @throws Exception + */ + public function __call($method, $args) + { + $proxy = $this->getPluginProxy(); + if (!$proxy) { + throw new Exception('Call to undefined method ' . __CLASS__ . '::' . $method . '()'); + } + + return call_user_func_array($proxy->getCallback($method), $args); + } + + /** + * Convert plugin name from `auto_escape` to `AutoEscape`. + * @param string $input + * @param string $separator + * + * @return mixed + */ + public static function toCamelCase($input, $separator = '_') + { + return join(array_map('ucfirst', explode($separator, $input))); + + // TODO >= PHP5.4.32 + //return str_replace($separator, '', ucwords($input, $separator)); + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Data.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Data.php new file mode 100644 index 0000000..85226e7 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Data.php @@ -0,0 +1,264 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.4 + * @date 2017-03-01 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Dwoo data object, use it for complex data assignments or if you want to easily pass it + * around multiple functions to avoid passing an array by reference. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class Data implements IDataProvider +{ + /** + * Data array. + * + * @var array + */ + protected $data = array(); + + /** + * Returns the data array. + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Clears a the entire data or only the given key. + * + * @param array|string $name clears only one value if you give a name, multiple values if + * you give an array of names, or the entire data if left null + */ + public function clear($name = null) + { + if ($name === null) { + $this->data = array(); + } elseif (is_array($name)) { + foreach ($name as $index) { + unset($this->data[$index]); + } + } else { + unset($this->data[$name]); + } + } + + /** + * Overwrites the entire data with the given array. + * + * @param array $data the new data array to use + */ + public function setData(array $data) + { + $this->data = $data; + } + + /** + * merges the given array(s) with the current data with array_merge. + * + * @param array $data the array to merge + */ + public function mergeData(array $data) + { + $args = func_get_args(); + foreach ($args as $key => $v) { + if (is_array($v)) { + $this->data = array_merge($this->data, $v); + } + } + } + + /** + * Assigns a value or an array of values to the data object. + * + * @param array|string $name an associative array of multiple (index=>value) or a string + * that is the index to use, i.e. a value assigned to "foo" will be + * accessible in the template through {$foo} + * @param mixed $val the value to assign, or null if $name was an array + */ + public function assign($name, $val = null) + { + if (is_array($name)) { + reset($name); + foreach ($name as $k => $v){ + $this->data[$k] = $v; + } + } else { + $this->data[$name] = $val; + } + } + + /** + * Allows to assign variables using the object syntax. + * + * @param string $name the variable name + * @param string $value the value to assign to it + */ + public function __set($name, $value) + { + $this->assign($name, $value); + } + + /** + * Assigns a value by reference to the data object. + * + * @param string $name the index to use, i.e. a value assigned to "foo" will be + * accessible in the template through {$foo} + * @param mixed $val the value to assign by reference + */ + public function assignByRef($name, &$val) + { + $this->data[$name] = &$val; + } + + /** + * Appends values or an array of values to the data object. + * + * @param array|string $name an associative array of multiple (index=>value) or a string + * that is the index to use, i.e. a value assigned to "foo" will be + * accessible in the template through {$foo} + * @param mixed $val the value to assign, or null if $name was an array + * @param bool $merge true to merge data or false to append, defaults to false + */ + public function append($name, $val = null, $merge = false) + { + if (is_array($name)) { + foreach ($name as $key => $val) { + if (isset($this->data[$key]) && !is_array($this->data[$key])) { + settype($this->data[$key], 'array'); + } + + if ($merge === true && is_array($val)) { + $this->data[$key] = $val + $this->data[$key]; + } else { + $this->data[$key][] = $val; + } + } + } elseif ($val !== null) { + if (isset($this->data[$name]) && !is_array($this->data[$name])) { + settype($this->data[$name], 'array'); + } elseif (!isset($this->data[$name])) { + $this->data[$name] = array(); + } + + if ($merge === true && is_array($val)) { + $this->data[$name] = $val + $this->data[$name]; + } else { + $this->data[$name][] = $val; + } + } + } + + /** + * Appends a value by reference to the data object. + * + * @param string $name the index to use, i.e. a value assigned to "foo" will be + * accessible in the template through {$foo} + * @param mixed $val the value to append by reference + * @param bool $merge true to merge data or false to append, defaults to false + */ + public function appendByRef($name, &$val, $merge = false) + { + if (isset($this->data[$name]) && !is_array($this->data[$name])) { + settype($this->data[$name], 'array'); + } + + if ($merge === true && is_array($val)) { + foreach ($val as $key => &$value) { + $this->data[$name][$key] = &$value; + } + } else { + $this->data[$name][] = &$val; + } + } + + /** + * Returns true if the variable has been assigned already, false otherwise. + * + * @param string $name the variable name + * + * @return bool + */ + public function isAssigned($name) + { + return isset($this->data[$name]); + } + + /** + * Supports calls to isset($dwoo->var). + * + * @param string $name the variable name + * + * @return bool + */ + public function __isset($name) + { + return isset($this->data[$name]); + } + + /** + * Unassigns/removes a variable. + * + * @param string $name the variable name + */ + public function unassign($name) + { + unset($this->data[$name]); + } + + /** + * Supports unsetting variables using the object syntax. + * + * @param string $name the variable name + */ + public function __unset($name) + { + unset($this->data[$name]); + } + + /** + * Returns a variable if it was assigned. + * + * @param string $name the variable name + * + * @return mixed + */ + public function get($name) + { + return $this->__get($name); + } + + /** + * Allows to read variables using the object syntax. + * + * @param string $name the variable name + * + * @return mixed + * @throws Exception + */ + public function __get($name) + { + if (isset($this->data[$name])) { + return $this->data[$name]; + } else { + throw new Exception('Tried to read a value that was not assigned yet : "' . $name . '"'); + } + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Exception.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Exception.php new file mode 100644 index 0000000..f61795f --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Exception.php @@ -0,0 +1,26 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Main dwoo exception class. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class Exception extends \Exception +{ +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Filter.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Filter.php new file mode 100644 index 0000000..5fdb4ed --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Filter.php @@ -0,0 +1,52 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Base class for filters. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +abstract class Filter +{ + /** + * The dwoo instance that runs this filter. + * + * @var Core + */ + protected $dwoo; + + /** + * Constructor, if you override it, call parent::__construct($dwoo); or assign + * the dwoo instance yourself if you need it. + * + * @param Core $dwoo the dwoo instance that runs this plugin + */ + public function __construct(Core $dwoo) + { + $this->dwoo = $dwoo; + } + + /** + * Processes the input and returns it filtered. + * + * @param string $input the template to process + * + * @return string + */ + abstract public function process($input); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompilable.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompilable.php new file mode 100644 index 0000000..40e1155 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompilable.php @@ -0,0 +1,31 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Interface that represents a compilable plugin. + * implement this to notify the compiler that this plugin does not need to be loaded at runtime. + * to implement it right, you must implement public static function compile(Compiler $compiler, $arg, $arg, + * ...), which replaces the process() method (that means compile() should have all arguments it + * requires). This software is provided 'as-is', without any express or implied warranty. In no event will the authors + * be held liable for any damages arising from the use of this software. + */ +interface ICompilable +{ + // this replaces the process function + //public static function compile(Compiler $compiler, $arg, $arg, ...); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompilable/Block.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompilable/Block.php new file mode 100644 index 0000000..241db4e --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompilable/Block.php @@ -0,0 +1,27 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\ICompilable; + +/** + * Interface that represents a compilable block plugin. + * implement this to notify the compiler that this plugin does not need to be loaded at runtime. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface Block +{ +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompiler.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompiler.php new file mode 100644 index 0000000..619aaab --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ICompiler.php @@ -0,0 +1,59 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE LGPLv3 + * @version 1.3.6 + * @date 2017-03-21 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +use Dwoo\Security\Policy as SecurityPolicy; + +/** + * Interface that represents a dwoo compiler. + * while implementing this is enough to interact with Dwoo/Templates, it is not + * sufficient to interact with Dwoo/Plugins, however the main purpose of creating a + * new compiler would be to interact with other/different plugins, that is why this + * interface has been left with the minimum requirements. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface ICompiler +{ + /** + * Compiles the provided string down to php code. + * + * @param Core $core + * @param ITemplate $template the template to compile + * + * @return string a compiled php code string + */ + public function compile(Core $core, ITemplate $template); + + /** + * Adds the custom plugins loaded into Dwoo to the compiler so it can load them. + * + * @see Core::addPlugin + * + * @param array $customPlugins an array of custom plugins + */ + public function setCustomPlugins(array $customPlugins); + + /** + * Sets the security policy object to enforce some php security settings. + * use this if untrusted persons can modify templates, + * set it on the Dwoo object as it will be passed onto the compiler automatically + * + * @param SecurityPolicy $policy the security policy object + */ + public function setSecurityPolicy(SecurityPolicy $policy = null); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IDataProvider.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IDataProvider.php new file mode 100644 index 0000000..9392b2d --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IDataProvider.php @@ -0,0 +1,32 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Interface that represents a dwoo data object. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface IDataProvider +{ + /** + * Returns the data as an associative array that will be used in the template. + * + * @return array + */ + public function getData(); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IElseable.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IElseable.php new file mode 100644 index 0000000..b8f3483 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IElseable.php @@ -0,0 +1,30 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Interface that represents a block plugin that supports the else functionality. + * the else block will enter an "hasElse" parameter inside the parameters array + * of the closest parent implementing this interface, the hasElse parameter contains + * the else output that should be appended to the block's content (see foreach or other + * block for examples) + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface IElseable +{ +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ILoader.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ILoader.php new file mode 100644 index 0000000..febf202 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ILoader.php @@ -0,0 +1,37 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Interface for dwoo plugin loaders. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface ILoader +{ + /** + * Loads a plugin file. + * the second parameter is used to avoid permanent rehashing when using php functions, + * however this means that if you have add a plugin that overrides a php function you have + * to delete the classpath.cache file(s) by hand to force a rehash of the plugins + * + * @param string $class the plugin name, without the `Plugin` prefix + * @param bool $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it + * has just been added, defaults to true + */ + public function loadPlugin($class, $forceRehash = true); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IPluginProxy.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IPluginProxy.php new file mode 100644 index 0000000..d20f3f7 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/IPluginProxy.php @@ -0,0 +1,74 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Interface that represents a dwoo plugin proxy. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface IPluginProxy +{ + /** + * Returns true or false to say whether the given plugin is handled by this proxy or not. + * + * @param string $name the plugin name + * + * @return bool true if the plugin is known and usable, otherwise false + */ + public function handles($name); + + /** + * Returns the code (as a string) to call the plugin + * (this will be executed at runtime inside the Dwoo class). + * + * @param string $name the plugin name + * @param array $params a parameter array, array key "*" is the rest array + * + * @return string + */ + public function getCode($name, $params); + + /** + * Returns a callback to the plugin, this is used with the reflection API to + * find out about the plugin's parameter names etc. + * should you need a rest array without the possibility to edit the + * plugin's code, you can provide a callback to some + * other function with the correct parameter signature, i.e. : + * + * return array($this, "callbackHelper"); + * // and callbackHelper would be as such: + * public function callbackHelper(array $rest=array()){} + * + * + * @param string $name the plugin name + * + * @return callback + */ + public function getCallback($name); + + /** + * Returns some code that will check if the plugin is loaded and if not load it + * this is optional, if your plugins are autoloaded or whatever, just return an + * empty string. + * + * @param string $name the plugin name + * + * @return string + */ + public function getLoader($name); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ITemplate.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ITemplate.php new file mode 100644 index 0000000..6fa65f3 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/ITemplate.php @@ -0,0 +1,161 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.4 + * @date 2017-03-07 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Interface that represents a dwoo template. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +interface ITemplate +{ + /** + * Returns the cache duration for this template. + * defaults to null if it was not provided + * + * @return int|null + */ + public function getCacheTime(); + + /** + * Sets the cache duration for this template. + * can be used to set it after the object is created if you did not provide + * it in the constructor + * + * @param int $seconds duration of the cache validity for this template, if + * null it defaults to the Dwoo instance's cache time. 0 = disable and + * -1 = infinite cache + */ + public function setCacheTime($seconds = null); + + /** + * Returns the cached template output file name, true if it's cache-able but not cached + * or false if it's not cached. + * + * @param Core $core the dwoo instance that requests it + * + * @return string|bool + */ + public function getCachedTemplate(Core $core); + + /** + * Caches the provided output into the cache file. + * + * @param Core $core the dwoo instance that requests it + * @param string $output the template output + * + * @return mixed full path of the cached file or false upon failure + */ + public function cache(Core $core, $output); + + /** + * Clears the cached template if it's older than the given time. + * + * @param Core $core the dwoo instance that was used to cache that template + * @param int $olderThan minimum time (in seconds) required for the cache to be cleared + * + * @return bool true if the cache was not present or if it was deleted, false if it remains there + */ + public function clearCache(Core $core, $olderThan = - 1); + + /** + * Returns the compiled template file name. + * + * @param Core $core the dwoo instance that requests it + * @param ICompiler $compiler the compiler that must be used + * + * @return string + */ + public function getCompiledTemplate(Core $core, ICompiler $compiler = null); + + /** + * Returns the template name. + * + * @return string + */ + public function getName(); + + /** + * Returns the resource name for this template class. + * + * @return string + */ + public function getResourceName(); + + /** + * Returns the resource identifier for this template or false if it has no identifier. + * + * @return string|false + */ + public function getResourceIdentifier(); + + /** + * Returns the template source of this template. + * + * @return string + */ + public function getSource(); + + /** + * Returns an unique string identifying the current version of this template, + * for example a timestamp of the last modified date or a hash of the template source. + * + * @return string + */ + public function getUid(); + + /** + * Returns the compiler used by this template, if it was just compiled, or null. + * + * @return ICompiler + */ + public function getCompiler(); + + /** + * Returns some php code that will check if this template has been modified or not. + * if the function returns null, the template will be instanciated and then the Uid checked + * + * @return string + */ + public function getIsModifiedCode(); + + /** + * Returns a new template object from the given resource identifier, null if no include is + * possible (resource not found), or false if include is not permitted by this resource type. + * this method should also check if $dwoo->getSecurityPolicy() is null or not and do the + * necessary permission checks if required, if the security policy prevents the template + * generation it should throw a new Security\Exception with a relevant message + * + * @param Core $core + * @param mixed $resourceId the resource identifier + * @param int $cacheTime duration of the cache validity for this template, if null it defaults to the + * Dwoo instance that will render this template if null it defaults to the Dwoo + * instance that will render this template + * @param string $cacheId the unique cache identifier of this page or anything else that makes this + * template's content unique, if null it defaults to the current url makes this + * template's content unique, if null it defaults to the current url + * @param string $compileId the unique compiled identifier, which is used to distinguish this template from + * others, if null it defaults to the filename+bits of the path template from + * others, if null it defaults to the filename+bits of the path + * @param ITemplate $parentTemplate the template that is requesting a new template object (through an include, + * extends or any other plugin) an include, extends or any other plugin) + * + * @return ITemplate|false|null + */ + public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null, + $compileId = null, ITemplate $parentTemplate = null); +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Loader.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Loader.php new file mode 100644 index 0000000..7dde76e --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Loader.php @@ -0,0 +1,190 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE LGPLv3 + * @version 1.4.0 + * @date 2017-03-16 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Handles plugin loading and caching of plugins names/paths relationships. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class Loader implements ILoader +{ + /** + * Stores the plugin directories. + * + * @see addDirectory + * @var array + */ + protected $paths = array(); + + /** + * Stores the plugins names/paths relationships + * don't edit this on your own, use addDirectory. + * + * @see addDirectory + * @var array + */ + protected $classPath = array(); + + /** + * Path where class paths cache files are written. + * + * @var string + */ + protected $cacheDir; + + /** + * Path where builtin plugins are stored. + * + * @var string + */ + protected $corePluginDir; + + /** + * Loader constructor. + * + * @param $cacheDir + */ + public function __construct($cacheDir) + { + $this->corePluginDir = __DIR__ . DIRECTORY_SEPARATOR . 'Plugins'; + $this->cacheDir = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + + // include class paths or rebuild paths if the cache file isn't there + $cacheFile = $this->cacheDir . 'classpath.cache.d' . Core::RELEASE_TAG . '.php'; + + $chachedClassPath = null; + + if (file_exists($cacheFile)) { + + $chachedClassPath = unserialize(file_get_contents($cacheFile)); + + if (is_array($chachedClassPath)) { + $this->classPath = $chachedClassPath + $this->classPath; + } + } + + if (!is_array($chachedClassPath)) { + $this->rebuildClassPathCache($this->corePluginDir, $cacheFile); + } + } + + /** + * Rebuilds class paths, scans the given directory recursively and saves all paths in the given file. + * + * @param string $path the plugin path to scan + * @param string|boolean $cacheFile the file where to store the plugin paths cache, it will be overwritten + * + * @throws Exception + */ + protected function rebuildClassPathCache($path, $cacheFile) + { + $tmp = array(); + if ($cacheFile !== false) { + $tmp = $this->classPath; + $this->classPath = array(); + } + + // iterates over all files/folders + foreach (new \DirectoryIterator($path) as $fileInfo) { + if (!$fileInfo->isDot()) { + if ($fileInfo->isDir()) { + $this->rebuildClassPathCache($fileInfo->getPathname(), false); + } else { + $this->classPath[$fileInfo->getBasename('.php')] = $fileInfo->getPathname(); + } + } + } + + // save in file if it's the first call (not recursed) + if ($cacheFile !== false) { + if (!file_put_contents($cacheFile, serialize($this->classPath), LOCK_EX)) { + throw new Exception('Could not write into ' . $cacheFile . ', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()'); + } + $this->classPath += $tmp; + } + } + + /** + * Loads a plugin file. + * + * @param string $class the plugin name, without the `Plugin` prefix + * @param bool $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it + * has just been added, defaults to true + * + * @throws Exception + */ + public function loadPlugin($class, $forceRehash = true) + { + /** + * An unknown class was requested (maybe newly added) or the + * include failed so we rebuild the cache. include() will fail + * with an uncatchable error if the file doesn't exist, which + * usually means that the cache is stale and must be rebuilt, + * so we check for that before trying to include() the plugin. + */ + if ((!isset($this->classPath[$class]) || !is_readable($this->classPath[$class])) || (!isset + ($this->classPath[$class . 'Compile']) || !is_readable($this->classPath[$class . 'Compile']))) { + if ($forceRehash) { + $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d' . + Core::RELEASE_TAG . '.php'); + foreach ($this->paths as $path => $file) { + $this->rebuildClassPathCache($path, $file); + } + if (isset($this->classPath[$class])) { + include_once $this->classPath[$class]; + } elseif (isset($this->classPath[$class . 'Compile'])) { + include_once $this->classPath[$class . 'Compile']; + } else { + throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE); + } + } else { + throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE); + } + } + } + + /** + * Adds a plugin directory, the plugins found in the new plugin directory + * will take precedence over the other directories (including the default + * dwoo plugin directory), you can use this for example to override plugins + * in a specific directory for a specific application while keeping all your + * usual plugins in the same place for all applications. + * TOCOM don't forget that php functions overrides are not rehashed so you + * need to clear the classpath caches by hand when adding those. + * + * @param string $pluginDirectory the plugin path to scan + * + * @throws Exception + */ + public function addDirectory($pluginDirectory) + { + $pluginDir = realpath($pluginDirectory); + if (!$pluginDir) { + throw new Exception('Plugin directory does not exist or can not be read : ' . $pluginDirectory); + } + $cacheFile = $this->cacheDir . 'classpath-' . substr(strtr($pluginDir, '/\\:' . PATH_SEPARATOR, '----'), + strlen($pluginDir) > 80 ? - 80 : 0) . '.d' . Core::RELEASE_TAG . '.php'; + $this->paths[$pluginDir] = $cacheFile; + if (file_exists($cacheFile)) { + $classpath = file_get_contents($cacheFile); + $this->classPath = unserialize($classpath) + $this->classPath; + } else { + $this->rebuildClassPathCache($pluginDir, $cacheFile); + } + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugin.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugin.php new file mode 100644 index 0000000..c607806 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugin.php @@ -0,0 +1,105 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-23 + * @link http://dwoo.org/ + */ + +namespace Dwoo; + +/** + * Base plugin class. + * you have to implement the process() method, it will receive the parameters that + * are in the template code + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +abstract class Plugin +{ + /** + * The dwoo instance that runs this plugin. + * + * @var Core + */ + protected $core; + + /** + * Constructor, if you override it, call parent::__construct($core); or assign + * the dwoo instance yourself if you need it. + * + * @param Core $core the dwoo instance that runs this plugin + */ + public function __construct(Core $core) + { + $this->core = $core; + } + + // plugins should always implement : + // public function process($arg, $arg, ...) + // or for block plugins : + // public function init($arg, $arg, ...) + + // this could be enforced with : + // abstract public function process(...); + // if my feature request gets enough interest one day + // see => http://bugs.php.net/bug.php?id=44043 + + /** + * Utility function that converts an array of compiled parameters (or rest array) to a string of xml/html tag + * attributes. this is to be used in preProcessing or postProcessing functions, example : + * $p = $compiler->getCompiledParams($params); + * // get only the rest array as attributes + * $attributes = Plugin::paramsToAttributes($p['*']); + * // get all the parameters as attributes (if there is a rest array, it will be included) + * $attributes = Plugin::paramsToAttributes($p); + * + * @param array $params an array of attributeName=>value items that will be compiled to be ready for inclusion in a php string + * inclusion in a php string + * @param string $delim the string delimiter you want to use (defaults to ') + * @param Compiler $compiler the compiler instance (optional for BC, but recommended to pass it for proper escaping behavior) + * escaping behavior) + * + * @return string + */ + public static function paramsToAttributes(array $params, $delim = '\'', Compiler $compiler = null) + { + if (isset($params['*'])) { + $params = array_merge($params, $params['*']); + unset($params['*']); + } + + $out = ''; + foreach ($params as $attr => $val) { + $out .= ' ' . $attr . '='; + if (trim($val, '"\'') == '' || $val == 'null') { + $out .= str_replace($delim, '\\' . $delim, '""'); + } elseif (substr($val, 0, 1) === $delim && substr($val, - 1) === $delim) { + $out .= str_replace($delim, '\\' . $delim, '"' . substr($val, 1, - 1) . '"'); + } else { + if (!$compiler) { + // disable double encoding since it can not be determined if it was encoded + $escapedVal = '.(is_string($tmp2=' . $val . ') ? htmlspecialchars($tmp2, ENT_QUOTES, $this->charset, false) : $tmp2).'; + } elseif (!$compiler->getAutoEscape() || false === strpos($val, 'isset($this->scope')) { + // escape if auto escaping is disabled, or there was no variable in the string + $escapedVal = '.(is_string($tmp2=' . $val . ') ? htmlspecialchars($tmp2, ENT_QUOTES, $this->charset) : $tmp2).'; + } else { + // print as is + $escapedVal = '.' . $val . '.'; + } + + $out .= str_replace($delim, '\\' . $delim, '"') . $delim . $escapedVal . $delim . str_replace($delim, '\\' . $delim, '"'); + } + } + + return ltrim($out); + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginA.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginA.php new file mode 100644 index 0000000..6210d84 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginA.php @@ -0,0 +1,95 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Outputs a html <a> tag + *
    + *  * href : the target URI where the link must point
    + *  * rest : any other attributes you want to add to the tag can be added as named parameters
    + * 
    . + * Example : + * + * {* Create a simple link out of an url variable and add a special class attribute: *} + * {a $url class="external" /} + * {* Mark a link as active depending on some other variable : *} + * {a $link.url class=tif($link.active "active"); $link.title /} + * {* This is similar to: {$link.title} *} + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginA extends BlockPlugin implements ICompilableBlock +{ + /** + * @param $href + * @param array $rest + */ + public function init($href, array $rest = array()) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $p = $compiler->getCompiledParams($params); + + $out = Compiler::PHP_OPEN . 'echo \'\';' . Compiler::PHP_CLOSE; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $p = $compiler->getCompiledParams($params); + + // no content was provided so use the url as display text + if ($content == '') { + // merge into the href if href is a string + if (substr($p['href'], - 1) === '"' || substr($p['href'], - 1) === '\'') { + return Compiler::PHP_OPEN . 'echo ' . substr($p['href'], 0, - 1) . '' . substr($p['href'], - 1) . ';' . Compiler::PHP_CLOSE; + } + + // otherwise append + return Compiler::PHP_OPEN . 'echo ' . $p['href'] . '.\'\';' . Compiler::PHP_CLOSE; + } + + // return content + return $content . ''; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginAutoEscape.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginAutoEscape.php new file mode 100644 index 0000000..7b45ec9 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginAutoEscape.php @@ -0,0 +1,98 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; +use Dwoo\Compilation\Exception as CompilationException; + +/** + * Overrides the compiler auto-escape setting within the block + *
    + *  * enabled : if set to "on", "enable", true or 1 then the compiler autoescaping is enabled inside this block. set to
    + *  "off", "disable", false or 0 to disable it
    + * 
    + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginAutoEscape extends BlockPlugin implements ICompilableBlock +{ + protected static $stack = array(); + + /** + * @param $enabled + */ + public function init($enabled) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + * @throws CompilationException + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $params = $compiler->getCompiledParams($params); + switch (strtolower(trim((string)$params['enabled'], '"\''))) { + + case 'on': + case 'true': + case 'enabled': + case 'enable': + case '1': + $enable = true; + break; + case 'off': + case 'false': + case 'disabled': + case 'disable': + case '0': + $enable = false; + break; + default: + throw new CompilationException($compiler, 'Auto_Escape : Invalid parameter (' . $params['enabled'] . '), valid parameters are "enable"/true or "disable"/false'); + } + + self::$stack[] = $compiler->getAutoEscape(); + $compiler->setAutoEscape($enable); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $compiler->setAutoEscape(array_pop(self::$stack)); + + return $content; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginBlock.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginBlock.php new file mode 100644 index 0000000..7e66182 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginBlock.php @@ -0,0 +1,66 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * This is used only when rendering a template that has blocks but is not extending anything, + * it doesn't do anything by itself and should not be used outside of template inheritance context, + * see {@link http://wiki.dwoo.org/index.php/TemplateInheritance} to read more about it. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginBlock extends BlockPlugin implements ICompilableBlock +{ + /** + * @param string $name + */ + public function init($name = '') + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + return $content; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginCapture.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginCapture.php new file mode 100644 index 0000000..f8c9c03 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginCapture.php @@ -0,0 +1,95 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Captures all the output within this block and saves it into {$.capture.default} by default, + * or {$.capture.name} if you provide another name. + *
    + *  * name : capture name, used to read the value afterwards
    + *  * assign : if set, the value is also saved in the given variable
    + *  * cat : if true, the value is appended to the previous one (if any) instead of overwriting it
    + * 
    + * If the cat parameter is true, the content + * will be appended to the existing content. + * Example : + * + * {capture "foo"} + * Anything in here won't show, it will be saved for later use.. + * {/capture} + * Output was : {$.capture.foo} + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginCapture extends BlockPlugin implements ICompilableBlock +{ + /** + * @param string $name + * @param null $assign + * @param bool $cat + * @param bool $trim + */ + public function init($name = 'default', $assign = null, $cat = false, $trim = false) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return Compiler::PHP_OPEN . $prepend . 'ob_start();' . $append . Compiler::PHP_CLOSE; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $params = $compiler->getCompiledParams($params); + + $out = $content . Compiler::PHP_OPEN . $prepend . "\n" . '$tmp = ob_get_clean();'; + if ($params['trim'] !== 'false' && $params['trim'] !== 0) { + $out .= "\n" . '$tmp = trim($tmp);'; + } + if ($params['cat'] === 'true' || $params['cat'] === 1) { + $out .= "\n" . '$tmp = $this->readVar(\'dwoo.capture.\'.' . $params['name'] . ') . $tmp;'; + } + if ($params['assign'] !== 'null') { + $out .= "\n" . '$this->scope[' . $params['assign'] . '] = $tmp;'; + } + + return $out . "\n" . '$this->globals[\'capture\'][' . $params['name'] . '] = $tmp;' . $append . Compiler::PHP_CLOSE; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginDynamic.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginDynamic.php new file mode 100644 index 0000000..3b1f61a --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginDynamic.php @@ -0,0 +1,116 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; +use Dwoo\Compilation\Exception as CompilationException; + +/** + * Marks the contents of the block as dynamic. Which means that it will not be cached. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginDynamic extends BlockPlugin implements ICompilableBlock +{ + /** + * + */ + public function init() + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + try { + $compiler->findBlock('dynamic'); + + return $content; + } + catch (CompilationException $e) { + } + $output = Compiler::PHP_OPEN . 'if($doCache) {' . "\n\t" . 'echo \'' . str_replace('\'', '\\\'', $content) . '\';' . "\n} else {\n\t"; + if (substr($content, 0, strlen(Compiler::PHP_OPEN)) == Compiler::PHP_OPEN) { + $output .= substr($content, strlen(Compiler::PHP_OPEN)); + } else { + $output .= Compiler::PHP_CLOSE . $content; + } + if (substr($output, - strlen(Compiler::PHP_CLOSE)) == Compiler::PHP_CLOSE) { + $output = substr($output, 0, - strlen(Compiler::PHP_CLOSE)); + } else { + $output .= Compiler::PHP_OPEN; + } + $output .= "\n}" . Compiler::PHP_CLOSE; + + return $output; + } + + /** + * @param $output + * @param $dynamicId + * @param $compiledFile + * + * @return mixed|string + */ + public static function unescape($output, $dynamicId, $compiledFile) + { + $output = preg_replace_callback('/(.+?)<\/dwoo:dynamic_' . $dynamicId . '>/s', array( + 'self', + 'unescapePhp' + ), $output, - 1, $count); + // re-add the includes on top of the file + if ($count && preg_match('#/\* template head \*/(.+?)/\* end template head \*/#s', file_get_contents($compiledFile), $m)) { + $output = '' . $output; + } + + return $output; + } + + /** + * @param $match + * + * @return mixed + */ + public static function unescapePhp($match) + { + return preg_replace('{<\?php /\*' . $match[1] . '\*/ echo \'(.+?)\'; \?>}s', '$1', $match[2]); + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginElse.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginElse.php new file mode 100644 index 0000000..14143a2 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginElse.php @@ -0,0 +1,96 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; +use Dwoo\Compilation\Exception as CompilationException; + +/** + * Generic else block, it supports all builtin optional-display blocks which are if/for/foreach/loop/with. + * If any of those block contains an else statement, the content between {else} and {/block} (you do not + * need to close the else block) will be shown if the block's condition has no been met + * Example : + * + * {foreach $array val} + * $array is not empty so we display it's values : {$val} + * {else} + * if this shows, it means that $array is empty or doesn't exist. + * {/foreach} + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginElse extends BlockPlugin implements ICompilableBlock +{ + public function init() + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + * @throws CompilationException + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $preContent = ''; + while (true) { + $preContent .= $compiler->removeTopBlock(); + $block = &$compiler->getCurrentBlock(); + if (!$block) { + throw new CompilationException($compiler, 'An else block was found but it was not preceded by an if or other else-able construct'); + } + $interfaces = class_implements($block['class']); + if (in_array('Dwoo\IElseable', $interfaces) !== false) { + break; + } + } + + $params['initialized'] = true; + $compiler->injectBlock($type, $params); + + return $preContent; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + if (!isset($params['initialized'])) { + return ''; + } + + $block = &$compiler->getCurrentBlock(); + $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE; + + return ''; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginElseif.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginElseif.php new file mode 100644 index 0000000..033aa59 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginElseif.php @@ -0,0 +1,95 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\IElseable; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Acts as a php elseif block, allowing you to add one more condition + * if the previous one(s) didn't match. See the {if} plugin for syntax details. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginElseif extends PluginIf implements ICompilableBlock, IElseable +{ + /** + * @param array $rest + */ + public function init(array $rest) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $preContent = ''; + while (true) { + $preContent .= $compiler->removeTopBlock(); + $block = &$compiler->getCurrentBlock(); + $interfaces = class_implements($block['class']); + if (in_array('Dwoo\IElseable', $interfaces) !== false) { + break; + } + } + + $params['initialized'] = true; + $compiler->injectBlock($type, $params); + + return $preContent; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + if (!isset($params['initialized'])) { + return ''; + } + + $tokens = $compiler->getParamTokens($params); + $params = $compiler->getCompiledParams($params); + + $pre = Compiler::PHP_OPEN . 'elseif (' . implode(' ', self::replaceKeywords($params['*'], $tokens['*'], $compiler)) . ") {\n" . Compiler::PHP_CLOSE; + $post = Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE; + + if (isset($params['hasElse'])) { + $post .= $params['hasElse']; + } + + $block = &$compiler->getCurrentBlock(); + $block['params']['hasElse'] = $pre . $content . $post; + + return ''; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginFor.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginFor.php new file mode 100644 index 0000000..b696094 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginFor.php @@ -0,0 +1,192 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\IElseable; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Similar to the php for block + *
    + *  * name : for name to access it's iterator variables through {$.for.name.var} see {@link
    + *  http://wiki.dwoo.org/index.php/IteratorVariables} for details
    + *  * from : array to iterate from (which equals 0) or a number as a start value
    + *  * to : value to stop iterating at (equals count($array) by default if you set an array in from)
    + *  * step : defines the incrementation of the pointer at each iteration
    + * 
    + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginFor extends BlockPlugin implements ICompilableBlock, IElseable +{ + public static $cnt = 0; + + /** + * @param $name + * @param $from + * @param null $to + * @param int $step + * @param int $skip + */ + public function init($name, $from, $to = null, $step = 1, $skip = 0) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + // get block params and save the current template pointer to use it in the postProcessing method + $currentBlock = &$compiler->getCurrentBlock(); + $currentBlock['params']['tplPointer'] = $compiler->getPointer(); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $params = $compiler->getCompiledParams($params); + $tpl = $compiler->getTemplateSource($params['tplPointer']); + + // assigns params + $from = $params['from']; + $name = $params['name']; + $step = $params['step']; + $to = $params['to']; + + // evaluates which global variables have to be computed + $varName = '$dwoo.for.' . trim($name, '"\'') . '.'; + $shortVarName = '$.for.' . trim($name, '"\'') . '.'; + $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false; + $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false; + $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false; + $usesIndex = strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false; + $usesIteration = $usesFirst || $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false; + $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false; + $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false; + + if (strpos($name, '$this->scope[') !== false) { + $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true; + } + + // gets foreach id + $cnt = self::$cnt ++; + + // builds pre processing output for + $out = Compiler::PHP_OPEN . "\n" . '$_for' . $cnt . '_from = ' . $from . ';' . "\n" . '$_for' . $cnt . '_to = ' . $to . ';' . "\n" . '$_for' . $cnt . '_step = abs(' . $step . ');' . "\n" . 'if (is_numeric($_for' . $cnt . '_from) && !is_numeric($_for' . $cnt . '_to)) { $this->triggerError(\'For requires the to parameter when using a numerical from\'); }' . "\n" . '$tmp_shows = $this->isArray($_for' . $cnt . '_from, true) || (is_numeric($_for' . $cnt . '_from) && (abs(($_for' . $cnt . '_from - $_for' . $cnt . '_to)/$_for' . $cnt . '_step) !== 0 || $_for' . $cnt . '_from == $_for' . $cnt . '_to));'; + // adds for properties + if ($usesAny) { + $out .= "\n" . '$this->globals["for"][' . $name . '] = array' . "\n("; + if ($usesIndex) { + $out .= "\n\t" . '"index" => 0,'; + } + if ($usesIteration) { + $out .= "\n\t" . '"iteration" => 1,'; + } + if ($usesFirst) { + $out .= "\n\t" . '"first" => null,'; + } + if ($usesLast) { + $out .= "\n\t" . '"last" => null,'; + } + if ($usesShow) { + $out .= "\n\t" . '"show" => $tmp_shows,'; + } + if ($usesTotal) { + $out .= "\n\t" . '"total" => $this->isArray($_for' . $cnt . '_from) ? floor($this->count($_for' . $cnt . '_from) / $_for' . $cnt . '_step) : (is_numeric($_for' . $cnt . '_from) ? abs(($_for' . $cnt . '_to + 1 - $_for' . $cnt . '_from)/$_for' . $cnt . '_step) : 0),'; + } + $out .= "\n);\n" . '$_for' . $cnt . '_glob =& $this->globals["for"][' . $name . '];'; + } + // checks if for must be looped + $out .= "\n" . 'if ($tmp_shows)' . "\n{"; + // set from/to to correct values if an array was given + $out .= "\n\t" . 'if ($this->isArray($_for' . $cnt . '_from' . (isset($params['hasElse']) ? ', true' : '') . ') == true) { + $_for' . $cnt . '_to = is_numeric($_for' . $cnt . '_to) ? $_for' . $cnt . '_to - $_for' . $cnt . '_step : $this->count($_for' . $cnt . '_from) - 1; + $_for' . $cnt . '_from = 0; + }'; + + // if input are pure numbers it shouldn't reorder them, if it's variables it gets too messy though so in that case a counter should be used + $reverse = false; + $condition = '<='; + $incrementer = '+'; + + if (preg_match('{^(["\']?)([0-9]+)\1$}', $from, $mN1) && preg_match('{^(["\']?)([0-9]+)\1$}', $to, $mN2)) { + $from = (int)$mN1[2]; + $to = (int)$mN2[2]; + if ($from > $to) { + $reverse = true; + $condition = '>='; + $incrementer = '-'; + } + } + + // reverse from and to if needed + if (!$reverse) { + $out .= "\n\t" . 'if ($_for' . $cnt . '_from > $_for' . $cnt . '_to) { + $tmp = $_for' . $cnt . '_from; + $_for' . $cnt . '_from = $_for' . $cnt . '_to; + $_for' . $cnt . '_to = $tmp; + }'; + } + + $out .= "\n\t" . 'for ($this->scope[' . $name . '] = $_for' . $cnt . '_from; $this->scope[' . $name . '] ' . $condition . ' $_for' . $cnt . '_to; $this->scope[' . $name . '] ' . $incrementer . '= $_for' . $cnt . '_step)' . "\n\t{"; + // updates properties + if ($usesIndex) { + $out .= "\n\t\t" . '$_for' . $cnt . '_glob["index"] = $this->scope[' . $name . '];'; + } + if ($usesFirst) { + $out .= "\n\t\t" . '$_for' . $cnt . '_glob["first"] = (string) ($_for' . $cnt . '_glob["iteration"] === 1);'; + } + if ($usesLast) { + $out .= "\n\t\t" . '$_for' . $cnt . '_glob["last"] = (string) ($_for' . $cnt . '_glob["iteration"] === $_for' . $cnt . '_glob["total"]);'; + } + $out .= "\n/* -- for start output */\n" . Compiler::PHP_CLOSE; + + // build post processing output and cache it + $postOut = Compiler::PHP_OPEN . '/* -- for end output */'; + // update properties + if ($usesIteration) { + $postOut .= "\n\t\t" . '$_for' . $cnt . '_glob["iteration"]+=1;'; + } + // end loop + $postOut .= "\n\t}\n}\n" . Compiler::PHP_CLOSE; + + if (isset($params['hasElse'])) { + $postOut .= $params['hasElse']; + } + + return $out . $content . $postOut; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForeach.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForeach.php new file mode 100644 index 0000000..3817d7f --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForeach.php @@ -0,0 +1,201 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\IElseable; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; +use Dwoo\Compilation\Exception as CompilationException; + +/** + * Similar to the php foreach block, loops over an array. + * Note that if you don't provide the item parameter, the key will act as item + *
    + *  * from : the array that you want to iterate over
    + *  * key : variable name for the key (or for the item if item is not defined)
    + *  * item : variable name for each item
    + *  * name : foreach name to access it's iterator variables through {$.foreach.name.var} see {@link
    + *  http://wiki.dwoo.org/index.php/IteratorVariables} for details
    + * 
    + * Example : + * + * {foreach $array val} + * {$val.something} + * {/foreach} + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginForeach extends BlockPlugin implements ICompilableBlock, IElseable +{ + public static $cnt = 0; + + /** + * @param $from + * @param null $key + * @param null $item + * @param string $name + * @param null $implode + */ + public function init($from, $key = null, $item = null, $name = 'default', $implode = null) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + // get block params and save the current template pointer to use it in the postProcessing method + $currentBlock = &$compiler->getCurrentBlock(); + $currentBlock['params']['tplPointer'] = $compiler->getPointer(); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + * @throws CompilationException + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $params = $compiler->getCompiledParams($params); + $tpl = $compiler->getTemplateSource($params['tplPointer']); + + // assigns params + $src = $params['from']; + + if ($params['item'] !== 'null') { + if ($params['key'] !== 'null') { + $key = $params['key']; + } + $val = $params['item']; + } elseif ($params['key'] !== 'null') { + $val = $params['key']; + } else { + throw new CompilationException($compiler, 'Foreach item parameter missing'); + } + $name = $params['name']; + + if (substr($val, 0, 1) !== '"' && substr($val, 0, 1) !== '\'') { + throw new CompilationException($compiler, 'Foreach item parameter must be of type string'); + } + if (isset($key) && substr($val, 0, 1) !== '"' && substr($val, 0, 1) !== '\'') { + throw new CompilationException($compiler, 'Foreach key parameter must be of type string'); + } + + // evaluates which global variables have to be computed + $varName = '$dwoo.foreach.' . trim($name, '"\'') . '.'; + $shortVarName = '$.foreach.' . trim($name, '"\'') . '.'; + $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false; + $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false; + $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false; + $usesIndex = $usesFirst || strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false; + $usesIteration = $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false; + $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false; + $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false; + + if (strpos($name, '$this->scope[') !== false) { + $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true; + } + + // override globals vars if implode is used + if ($params['implode'] !== 'null') { + $implode = $params['implode']; + $usesAny = true; + $usesLast = true; + $usesIteration = true; + $usesTotal = true; + } + + // gets foreach id + $cnt = self::$cnt ++; + + // build pre content output + $pre = Compiler::PHP_OPEN . "\n" . '$_fh' . $cnt . '_data = ' . $src . ';'; + // adds foreach properties + if ($usesAny) { + $pre .= "\n" . '$this->globals["foreach"][' . $name . '] = array' . "\n("; + if ($usesIndex) { + $pre .= "\n\t" . '"index" => 0,'; + } + if ($usesIteration) { + $pre .= "\n\t" . '"iteration" => 1,'; + } + if ($usesFirst) { + $pre .= "\n\t" . '"first" => null,'; + } + if ($usesLast) { + $pre .= "\n\t" . '"last" => null,'; + } + if ($usesShow) { + $pre .= "\n\t" . '"show" => $this->isArray($_fh' . $cnt . '_data, true),'; + } + if ($usesTotal) { + $pre .= "\n\t" . '"total" => $this->count($_fh' . $cnt . '_data),'; + } + $pre .= "\n);\n" . '$_fh' . $cnt . '_glob =& $this->globals["foreach"][' . $name . '];'; + } + // checks if foreach must be looped + $pre .= "\n" . 'if ($this->isTraversable($_fh' . $cnt . '_data' . (isset($params['hasElse']) ? ', true' : '') . ') == true)' . "\n{"; + // iterates over keys + $pre .= "\n\t" . 'foreach ($_fh' . $cnt . '_data as ' . (isset($key) ? '$this->scope[' . $key . ']=>' : '') . '$this->scope[' . $val . '])' . "\n\t{"; + // updates properties + if ($usesFirst) { + $pre .= "\n\t\t" . '$_fh' . $cnt . '_glob["first"] = (string) ($_fh' . $cnt . '_glob["index"] === 0);'; + } + if ($usesLast) { + $pre .= "\n\t\t" . '$_fh' . $cnt . '_glob["last"] = (string) ($_fh' . $cnt . '_glob["iteration"] === $_fh' . $cnt . '_glob["total"]);'; + } + $pre .= "\n/* -- foreach start output */\n" . Compiler::PHP_CLOSE; + + // build post content output + $post = Compiler::PHP_OPEN . "\n"; + + if (isset($implode)) { + $post .= '/* -- implode */' . "\n" . 'if (!$_fh' . $cnt . '_glob["last"]) {' . "\n\t" . 'echo ' . $implode . ";\n}\n"; + } + $post .= '/* -- foreach end output */'; + // update properties + if ($usesIndex) { + $post .= "\n\t\t" . '$_fh' . $cnt . '_glob["index"]+=1;'; + } + if ($usesIteration) { + $post .= "\n\t\t" . '$_fh' . $cnt . '_glob["iteration"]+=1;'; + } + // end loop + $post .= "\n\t}\n}" . Compiler::PHP_CLOSE; + if (isset($params['hasElse'])) { + $post .= $params['hasElse']; + } + + return $pre . $content . $post; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForeachelse.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForeachelse.php new file mode 100644 index 0000000..5a09327 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForeachelse.php @@ -0,0 +1,73 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * This plugin serves as a {else} block specifically for the {foreach} plugin. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginForeachelse extends BlockPlugin implements ICompilableBlock +{ + public function init() + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $with = &$compiler->findBlock('foreach', true); + + $params['initialized'] = true; + $compiler->injectBlock($type, $params); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + if (!isset($params['initialized'])) { + return ''; + } + + $block = &$compiler->getCurrentBlock(); + $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE; + + return ''; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForelse.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForelse.php new file mode 100644 index 0000000..4d4a588 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginForelse.php @@ -0,0 +1,73 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * This plugin serves as a {else} block specifically for the {for} plugin. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginForelse extends BlockPlugin implements ICompilableBlock +{ + public function init() + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $with = &$compiler->findBlock('for', true); + + $params['initialized'] = true; + $compiler->injectBlock($type, $params); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + if (!isset($params['initialized'])) { + return ''; + } + + $block = &$compiler->getCurrentBlock(); + $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE; + + return ''; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginIf.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginIf.php new file mode 100644 index 0000000..d982327 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginIf.php @@ -0,0 +1,274 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\IElseable; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; +use Dwoo\Compilation\Exception as CompilationException; + +/** + * Conditional block, the syntax is very similar to the php one, allowing () || && and + * other php operators. Additional operators and their equivalent php syntax are as follow :. + * eq -> == + * neq or ne -> != + * gte or ge -> >= + * lte or le -> <= + * gt -> > + * lt -> < + * mod -> % + * not -> ! + * X is [not] div by Y -> (X % Y) == 0 + * X is [not] even [by Y] -> (X % 2) == 0 or ((X/Y) % 2) == 0 + * X is [not] odd [by Y] -> (X % 2) != 0 or ((X/Y) % 2) != 0 + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginIf extends BlockPlugin implements ICompilableBlock, IElseable +{ + /** + * @param array $rest + */ + public function init(array $rest) + { + } + + /** + * @param array $params + * @param array $tokens + * @param Compiler $compiler + * + * @return array + * @throws CompilationException + */ + public static function replaceKeywords(array $params, array $tokens, Compiler $compiler) + { + $p = array(); + + reset($params); + while (list($k, $v) = each($params)) { + $v = (string)$v; + if (substr($v, 0, 1) === '"' || substr($v, 0, 1) === '\'') { + $vmod = strtolower(substr($v, 1, - 1)); + } else { + $vmod = strtolower($v); + } + switch ($vmod) { + + case 'and': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '&&'; + } else { + $p[] = $v; + } + break; + case 'or': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '||'; + } else { + $p[] = $v; + } + break; + case 'xor': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '^'; + } else { + $p[] = $v; + } + break; + case 'eq': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '=='; + } else { + $p[] = $v; + } + break; + case 'ne': + case 'neq': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '!='; + } else { + $p[] = $v; + } + break; + case 'gte': + case 'ge': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '>='; + } else { + $p[] = $v; + } + break; + case 'lte': + case 'le': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '<='; + } else { + $p[] = $v; + } + break; + case 'gt': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '>'; + } else { + $p[] = $v; + } + break; + case 'lt': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '<'; + } else { + $p[] = $v; + } + break; + case 'mod': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '%'; + } else { + $p[] = $v; + } + break; + case 'not': + if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) { + $p[] = '!'; + } else { + $p[] = $v; + } + break; + case '<>': + $p[] = '!='; + break; + case '==': + case '!=': + case '>=': + case '<=': + case '>': + case '<': + case '===': + case '!==': + case '%': + case '!': + case '^': + $p[] = $vmod; + break; + case 'is': + if ($tokens[$k] !== Compiler::T_UNQUOTED_STRING) { + $p[] = $v; + break; + } + if (isset($params[$k + 1]) && strtolower(trim($params[$k + 1], '"\'')) === 'not' && $tokens[$k + 1] === Compiler::T_UNQUOTED_STRING) { + $negate = true; + next($params); + } else { + $negate = false; + } + $ptr = 1 + (int)$negate; + if ($tokens[$k + $ptr] !== Compiler::T_UNQUOTED_STRING) { + break; + } + if (!isset($params[$k + $ptr])) { + $params[$k + $ptr] = ''; + } else { + $params[$k + $ptr] = trim($params[$k + $ptr], '"\''); + } + switch ($params[$k + $ptr]) { + + case 'div': + if (isset($params[$k + $ptr + 1]) && strtolower(trim($params[$k + $ptr + 1], '"\'')) === 'by') { + $p[] = ' % ' . $params[$k + $ptr + 2] . ' ' . ($negate ? '!' : '=') . '== 0'; + next($params); + next($params); + next($params); + } else { + throw new CompilationException($compiler, 'If : Syntax error : syntax should be "if $a is [not] div by $b", found ' . $params[$k - 1] . ' is ' . ($negate ? 'not ' : '') . 'div ' . $params[$k + $ptr + 1] . ' ' . $params[$k + $ptr + 2]); + } + break; + case 'even': + $a = array_pop($p); + if (isset($params[$k + $ptr + 1]) && strtolower(trim($params[$k + $ptr + 1], '"\'')) === 'by') { + $b = $params[$k + $ptr + 2]; + $p[] = '(' . $a . ' / ' . $b . ') % 2 ' . ($negate ? '!' : '=') . '== 0'; + next($params); + next($params); + } else { + $p[] = $a . ' % 2 ' . ($negate ? '!' : '=') . '== 0'; + } + next($params); + break; + case 'odd': + $a = array_pop($p); + if (isset($params[$k + $ptr + 1]) && strtolower(trim($params[$k + $ptr + 1], '"\'')) === 'by') { + $b = $params[$k + $ptr + 2]; + $p[] = '(' . $a . ' / ' . $b . ') % 2 ' . ($negate ? '=' : '!') . '== 0'; + next($params); + next($params); + } else { + $p[] = $a . ' % 2 ' . ($negate ? '=' : '!') . '== 0'; + } + next($params); + break; + default: + throw new CompilationException($compiler, 'If : Syntax error : syntax should be "if $a is [not] (div|even|odd) [by $b]", found ' . $params[$k - 1] . ' is ' . $params[$k + $ptr + 1]); + } + break; + default: + $p[] = $v; + } + } + + return $p; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $tokens = $compiler->getParamTokens($params); + $params = $compiler->getCompiledParams($params); + $pre = Compiler::PHP_OPEN . 'if (' . implode(' ', self::replaceKeywords($params['*'], $tokens['*'], $compiler)) . ") {\n" . Compiler::PHP_CLOSE; + + $post = Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE; + + if (isset($params['hasElse'])) { + $post .= $params['hasElse']; + } + + return $pre . $content . $post; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginLoop.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginLoop.php new file mode 100644 index 0000000..5efbf43 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginLoop.php @@ -0,0 +1,170 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\IElseable; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Loops over an array and moves the scope into each value, allowing for shorter loop constructs. + * Note that to access the array key within a loop block, you have to use the {$_key} variable, + * you can not specify it yourself. + *
    + *  * from : the array that you want to iterate over
    + *  * name : loop name to access it's iterator variables through {$.loop.name.var} see {@link
    + *  http://wiki.dwoo.org/index.php/IteratorVariables} for details
    + * 
    + * Example : + * instead of a foreach block such as : + * + * {foreach $variable value} + * {$value.foo} {$value.bar} + * {/foreach} + * + * you can do : + * + * {loop $variable} + * {$foo} {$bar} + * {/loop} + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginLoop extends BlockPlugin implements ICompilableBlock, IElseable +{ + public static $cnt = 0; + + /** + * @param $from + * @param string $name + */ + public function init($from, $name = 'default') + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + // get block params and save the current template pointer to use it in the postProcessing method + $currentBlock = &$compiler->getCurrentBlock(); + $currentBlock['params']['tplPointer'] = $compiler->getPointer(); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $params = $compiler->getCompiledParams($params); + $tpl = $compiler->getTemplateSource($params['tplPointer']); + + // assigns params + $src = $params['from']; + $name = $params['name']; + + // evaluates which global variables have to be computed + $varName = '$dwoo.loop.' . trim($name, '"\'') . '.'; + $shortVarName = '$.loop.' . trim($name, '"\'') . '.'; + $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false; + $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false; + $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false; + $usesIndex = $usesFirst || strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false; + $usesIteration = $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false; + $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false; + $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false; + + if (strpos($name, '$this->scope[') !== false) { + $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true; + } + + // gets foreach id + $cnt = self::$cnt ++; + + // builds pre processing output + $pre = Compiler::PHP_OPEN . "\n" . '$_loop' . $cnt . '_data = ' . $src . ';'; + // adds foreach properties + if ($usesAny) { + $pre .= "\n" . '$this->globals["loop"][' . $name . '] = array' . "\n("; + if ($usesIndex) { + $pre .= "\n\t" . '"index" => 0,'; + } + if ($usesIteration) { + $pre .= "\n\t" . '"iteration" => 1,'; + } + if ($usesFirst) { + $pre .= "\n\t" . '"first" => null,'; + } + if ($usesLast) { + $pre .= "\n\t" . '"last" => null,'; + } + if ($usesShow) { + $pre .= "\n\t" . '"show" => $this->isTraversable($_loop' . $cnt . '_data, true),'; + } + if ($usesTotal) { + $pre .= "\n\t" . '"total" => $this->count($_loop' . $cnt . '_data),'; + } + $pre .= "\n);\n" . '$_loop' . $cnt . '_glob =& $this->globals["loop"][' . $name . '];'; + } + // checks if the loop must be looped + $pre .= "\n" . 'if ($this->isTraversable($_loop' . $cnt . '_data' . (isset($params['hasElse']) ? ', true' : '') . ') == true)' . "\n{"; + // iterates over keys + $pre .= "\n\t" . 'foreach ($_loop' . $cnt . '_data as $tmp_key => $this->scope["-loop-"])' . "\n\t{"; + // updates properties + if ($usesFirst) { + $pre .= "\n\t\t" . '$_loop' . $cnt . '_glob["first"] = (string) ($_loop' . $cnt . '_glob["index"] === 0);'; + } + if ($usesLast) { + $pre .= "\n\t\t" . '$_loop' . $cnt . '_glob["last"] = (string) ($_loop' . $cnt . '_glob["iteration"] === $_loop' . $cnt . '_glob["total"]);'; + } + $pre .= "\n\t\t" . '$_loop' . $cnt . '_scope = $this->setScope(array("-loop-"));' . "\n/* -- loop start output */\n" . Compiler::PHP_CLOSE; + + // build post processing output and cache it + $post = Compiler::PHP_OPEN . "\n" . '/* -- loop end output */' . "\n\t\t" . '$this->setScope($_loop' . $cnt . '_scope, true);'; + // update properties + if ($usesIndex) { + $post .= "\n\t\t" . '$_loop' . $cnt . '_glob["index"]+=1;'; + } + if ($usesIteration) { + $post .= "\n\t\t" . '$_loop' . $cnt . '_glob["iteration"]+=1;'; + } + // end loop + $post .= "\n\t}\n}\n" . Compiler::PHP_CLOSE; + if (isset($params['hasElse'])) { + $post .= $params['hasElse']; + } + + return $pre . $content . $post; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginSection.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginSection.php new file mode 100644 index 0000000..4f75b26 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginSection.php @@ -0,0 +1,159 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\IElseable; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Compatibility plugin for smarty templates, do not use otherwise, this is deprecated. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginSection extends BlockPlugin implements ICompilableBlock, IElseable +{ + public static $cnt = 0; + + /** + * @param $name + * @param $loop + * @param null $start + * @param null $step + * @param null $max + * @param bool $show + */ + public function init($name, $loop, $start = null, $step = null, $max = null, $show = true) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $output = Compiler::PHP_OPEN; + $params = $compiler->getCompiledParams($params); + + // assigns params + $loop = $params['loop']; + $start = $params['start']; + $max = $params['max']; + $name = $params['name']; + $step = $params['step']; + $show = $params['show']; + + // gets unique id + $cnt = self::$cnt ++; + + $output .= '$this->globals[\'section\'][' . $name . '] = array();' . "\n" . '$_section' . $cnt . ' =& $this->globals[\'section\'][' . $name . '];' . "\n"; + + if ($loop !== 'null') { + $output .= '$_section' . $cnt . '[\'loop\'] = is_array($tmp = ' . $loop . ') ? count($tmp) : max(0, (int) $tmp);' . "\n"; + } else { + $output .= '$_section' . $cnt . '[\'loop\'] = 1;' . "\n"; + } + + if ($show !== 'null') { + $output .= '$_section' . $cnt . '[\'show\'] = ' . $show . ";\n"; + } else { + $output .= '$_section' . $cnt . '[\'show\'] = true;' . "\n"; + } + + if ($name !== 'null') { + $output .= '$_section' . $cnt . '[\'name\'] = ' . $name . ";\n"; + } else { + $output .= '$_section' . $cnt . '[\'name\'] = true;' . "\n"; + } + + if ($max !== 'null') { + $output .= '$_section' . $cnt . '[\'max\'] = (int)' . $max . ";\n" . 'if($_section' . $cnt . '[\'max\'] < 0) { $_section' . $cnt . '[\'max\'] = $_section' . $cnt . '[\'loop\']; }' . "\n"; + } else { + $output .= '$_section' . $cnt . '[\'max\'] = $_section' . $cnt . '[\'loop\'];' . "\n"; + } + + if ($step !== 'null') { + $output .= '$_section' . $cnt . '[\'step\'] = (int)' . $step . ' == 0 ? 1 : (int) ' . $step . ";\n"; + } else { + $output .= '$_section' . $cnt . '[\'step\'] = 1;' . "\n"; + } + + if ($start !== 'null') { + $output .= '$_section' . $cnt . '[\'start\'] = (int)' . $start . ";\n"; + } else { + $output .= '$_section' . $cnt . '[\'start\'] = $_section' . $cnt . '[\'step\'] > 0 ? 0 : $_section' . $cnt . '[\'loop\'] - 1;' . "\n" . 'if ($_section' . $cnt . '[\'start\'] < 0) { $_section' . $cnt . '[\'start\'] = max($_section' . $cnt . '[\'step\'] > 0 ? 0 : -1, $_section' . $cnt . '[\'loop\'] + $_section' . $cnt . '[\'start\']); } ' . "\n" . 'else { $_section' . $cnt . '[\'start\'] = min($_section' . $cnt . '[\'start\'], $_section' . $cnt . '[\'step\'] > 0 ? $_section' . $cnt . '[\'loop\'] : $_section' . $cnt . '[\'loop\'] -1); }' . "\n"; + } + + /* if ($usesAny) { + $output .= "\n".'$this->globals["section"]['.$name.'] = array'."\n("; + if ($usesIndex) $output .="\n\t".'"index" => 0,'; + if ($usesIteration) $output .="\n\t".'"iteration" => 1,'; + if ($usesFirst) $output .="\n\t".'"first" => null,'; + if ($usesLast) $output .="\n\t".'"last" => null,'; + if ($usesShow) $output .="\n\t".'"show" => ($this->isArray($_for'.$cnt.'_from, true)) || (is_numeric($_for'.$cnt.'_from) && $_for'.$cnt.'_from != $_for'.$cnt.'_to),'; + if ($usesTotal) $output .="\n\t".'"total" => $this->isArray($_for'.$cnt.'_from) ? $this->count($_for'.$cnt.'_from) - $_for'.$cnt.'_skip : (is_numeric($_for'.$cnt.'_from) ? abs(($_for'.$cnt.'_to + 1 - $_for'.$cnt.'_from)/$_for'.$cnt.'_step) : 0),'; + $out.="\n);\n".'$_section'.$cnt.'[\'glob\'] =& $this->globals["section"]['.$name.'];'."\n\n"; + } + */ + + $output .= 'if ($_section' . $cnt . '[\'show\']) {' . "\n"; + if ($start === 'null' && $step === 'null' && $max === 'null') { + $output .= ' $_section' . $cnt . '[\'total\'] = $_section' . $cnt . '[\'loop\'];' . "\n"; + } else { + $output .= ' $_section' . $cnt . '[\'total\'] = min(ceil(($_section' . $cnt . '[\'step\'] > 0 ? $_section' . $cnt . '[\'loop\'] - $_section' . $cnt . '[\'start\'] : $_section' . $cnt . '[\'start\'] + 1) / abs($_section' . $cnt . '[\'step\'])), $_section' . $cnt . '[\'max\']);' . "\n"; + } + $output .= ' if ($_section' . $cnt . '[\'total\'] == 0) {' . "\n" . ' $_section' . $cnt . '[\'show\'] = false;' . "\n" . ' }' . "\n" . '} else {' . "\n" . ' $_section' . $cnt . '[\'total\'] = 0;' . "\n}\n"; + $output .= 'if ($_section' . $cnt . '[\'show\']) {' . "\n"; + $output .= "\t" . 'for ($this->scope[' . $name . '] = $_section' . $cnt . '[\'start\'], $_section' . $cnt . '[\'iteration\'] = 1; ' . '$_section' . $cnt . '[\'iteration\'] <= $_section' . $cnt . '[\'total\']; ' . '$this->scope[' . $name . '] += $_section' . $cnt . '[\'step\'], $_section' . $cnt . '[\'iteration\']++) {' . "\n"; + $output .= "\t\t" . '$_section' . $cnt . '[\'rownum\'] = $_section' . $cnt . '[\'iteration\'];' . "\n"; + $output .= "\t\t" . '$_section' . $cnt . '[\'index_prev\'] = $this->scope[' . $name . '] - $_section' . $cnt . '[\'step\'];' . "\n"; + $output .= "\t\t" . '$_section' . $cnt . '[\'index_next\'] = $this->scope[' . $name . '] + $_section' . $cnt . '[\'step\'];' . "\n"; + $output .= "\t\t" . '$_section' . $cnt . '[\'first\'] = ($_section' . $cnt . '[\'iteration\'] == 1);' . "\n"; + $output .= "\t\t" . '$_section' . $cnt . '[\'last\'] = ($_section' . $cnt . '[\'iteration\'] == $_section' . $cnt . '[\'total\']);' . "\n"; + + $output .= Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN; + + $output .= "\n\t}\n} " . Compiler::PHP_CLOSE; + + if (isset($params['hasElse'])) { + $output .= $params['hasElse']; + } + + return $output; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginSmartyinterface.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginSmartyinterface.php new file mode 100644 index 0000000..14211e1 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginSmartyinterface.php @@ -0,0 +1,96 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2017 David Sanchez + * @license http://dwoo.org/LICENSE LGPLv3 + * @version 1.3.6 + * @date 2017-03-21 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Core; +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Smarty compatibility layer for block plugins, this is used internally and you should not call it. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginSmartyinterface extends BlockPlugin implements ICompilableBlock +{ + /** + * @param $__funcname + * @param $__functype + * @param array $rest + */ + public function init($__funcname, $__functype, array $rest = array()) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $params = $compiler->getCompiledParams($params); + $func = $params['__funcname']; + $pluginType = $params['__functype']; + $params = $params['*']; + + if ($pluginType & Core::CUSTOM_PLUGIN) { + $customPlugins = $compiler->getCore()->getCustomPlugins(); + $callback = $customPlugins[$func]['callback']; + if (is_array($callback)) { + if (is_object($callback[0])) { + $callback = '$this->customPlugins[\'' . $func . '\'][0]->' . $callback[1] . '('; + } else { + $callback = '' . $callback[0] . '::' . $callback[1] . '('; + } + } else { + $callback = $callback . '('; + } + } else { + $callback = 'smarty_block_' . $func . '('; + } + + $paramsOut = ''; + foreach ($params as $i => $p) { + $paramsOut .= var_export($i, true) . ' => ' . $p . ','; + } + + $curBlock = &$compiler->getCurrentBlock(); + $curBlock['params']['postOut'] = Compiler::PHP_OPEN . ' $_block_content = ob_get_clean(); $_block_repeat=false; echo ' . $callback . '$_tag_stack[count($_tag_stack)-1], $_block_content, $this, $_block_repeat); } array_pop($_tag_stack);' . Compiler::PHP_CLOSE; + + return Compiler::PHP_OPEN . $prepend . ' if (!isset($_tag_stack)){ $_tag_stack = array(); } $_tag_stack[] = array(' . $paramsOut . '); $_block_repeat=true; ' . $callback . '$_tag_stack[count($_tag_stack)-1], null, $this, $_block_repeat); while ($_block_repeat) { ob_start();' . Compiler::PHP_CLOSE; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + return $content . $params['postOut']; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginStrip.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginStrip.php new file mode 100644 index 0000000..5ee726d --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginStrip.php @@ -0,0 +1,89 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Strips the spaces at the beginning and end of each line and also the line breaks + *
    + *  * mode : sets the content being stripped, available mode are 'default' or 'js'
    + *    for javascript, which strips the comments to prevent syntax errors
    + * 
    . + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginStrip extends BlockPlugin implements ICompilableBlock +{ + /** + * @param string $mode + */ + public function init($mode = 'default') + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return mixed|string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $params = $compiler->getCompiledParams($params); + + $mode = trim($params['mode'], '"\''); + switch ($mode) { + case 'js': + case 'javascript': + $content = preg_replace('#(? + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-20 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Core; +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; +use Dwoo\Compilation\Exception as CompilationException; + +/** + * Defines a sub-template that can then be called (even recursively) with the defined arguments + *
    + *  * name : template name
    + *  * rest : list of arguments and optional default values
    + * 
    + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginTemplate extends BlockPlugin implements ICompilableBlock +{ + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + * @throws CompilationException + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $params = $compiler->getCompiledParams($params); + $parsedParams = array(); + if (!isset($params['*'])) { + $params['*'] = array(); + } + foreach ($params['*'] as $param => $defValue) { + if (is_numeric($param)) { + $param = $defValue; + $defValue = null; + } + $param = trim($param, '\'"'); + if (!preg_match('#^[a-z0-9_]+$#i', $param)) { + throw new CompilationException($compiler, 'Function : parameter names must contain only A-Z, 0-9 or _'); + } + $parsedParams[$param] = $defValue; + } + $params['name'] = substr($params['name'], 1, - 1); + $params['*'] = $parsedParams; + $params['uuid'] = uniqid(); + $compiler->addTemplatePlugin($params['name'], $parsedParams, $params['uuid']); + $currentBlock = &$compiler->getCurrentBlock(); + $currentBlock['params'] = $params; + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string|void + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $paramstr = 'Dwoo\Core $dwoo'; + $init = 'static $_callCnt = 0;' . "\n" . '$dwoo->scope[\' ' . $params['uuid'] . '\'.$_callCnt] = array();' . "\n" . '$_scope = $dwoo->setScope(array(\' ' . $params['uuid'] . '\'.($_callCnt++)));' . "\n"; + $cleanup = '/* -- template end output */ $dwoo->setScope($_scope, true);'; + foreach ($params['*'] as $param => $defValue) { + if ($defValue === null) { + $paramstr .= ', $' . $param; + } else { + $paramstr .= ', $' . $param . ' = ' . $defValue; + } + $init .= '$dwoo->scope[\'' . $param . '\'] = $' . $param . ";\n"; + } + $init .= '/* -- template start output */'; + + $funcName = 'Plugin' . Core::toCamelCase($params['name']) . Core::toCamelCase($params['uuid']); + + $search = array('$this->charset', '$this->', '$this,',); + $replacement = array('$dwoo->getCharset()', '$dwoo->', '$dwoo,',); + $content = str_replace($search, $replacement, $content); + + $body = 'if (!function_exists(\'' . $funcName . "')) {\nfunction " . $funcName . '(' . $paramstr . ') {' . "\n$init" . Compiler::PHP_CLOSE . $prepend . $content . $append . Compiler::PHP_OPEN . $cleanup . "\n}\n}"; + $compiler->addTemplatePlugin($params['name'], $params['*'], $params['uuid'], $body); + } + + /** + * @param $name + * @param array $rest + */ + public function init($name, array $rest = array()) + { + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginTextformat.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginTextformat.php new file mode 100644 index 0000000..09fc056 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginTextformat.php @@ -0,0 +1,119 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Block\Plugin as BlockPlugin; + +/** + * Formats a string to the given format, you can wrap lines at a certain + * length and indent them + *
    + *  * wrap : maximum line length
    + *  * wrap_char : the character(s) to use to break the line
    + *  * wrap_cut : if true, the words that are longer than $wrap are cut instead of overflowing
    + *  * indent : amount of $indent_char to insert before every line
    + *  * indent_char : character(s) to insert before every line
    + *  * indent_first : amount of additional $indent_char to insert before the first line of each paragraphs
    + *  * style : some predefined formatting styles that set up every required variables, can be "email" or "html"
    + *  * assign : if set, the formatted text is assigned to that variable instead of being output
    + * 
    + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginTextformat extends BlockPlugin +{ + protected $wrap; + protected $wrapChar; + protected $wrapCut; + protected $indent; + protected $indChar; + protected $indFirst; + protected $assign; + + /** + * @param int $wrap + * @param string $wrap_char + * @param bool $wrap_cut + * @param int $indent + * @param string $indent_char + * @param int $indent_first + * @param string $style + * @param string $assign + */ + public function init($wrap = 80, $wrap_char = "\r\n", $wrap_cut = false, $indent = 0, $indent_char = ' ', $indent_first = 0, $style = '', $assign = '') + { + if ($indent_char === 'tab') { + $indent_char = "\t"; + } + + switch ($style) { + + case 'email': + $wrap = 72; + $indent_first = 0; + break; + case 'html': + $wrap_char = '
    '; + $indent_char = $indent_char == "\t" ? '    ' : ' '; + break; + } + + $this->wrap = (int)$wrap; + $this->wrapChar = (string)$wrap_char; + $this->wrapCut = (bool)$wrap_cut; + $this->indent = (int)$indent; + $this->indChar = (string)$indent_char; + $this->indFirst = (int)$indent_first + $this->indent; + $this->assign = (string)$assign; + } + + /** + * @return string + */ + public function process() + { + // gets paragraphs + $pgs = explode("\n", str_replace(array( + "\r\n", + "\r" + ), "\n", $this->buffer)); + + while (list($i) = each($pgs)) { + if (empty($pgs[$i])) { + continue; + } + + // removes line breaks and extensive white space + $pgs[$i] = preg_replace(array( + '#\s+#', + '#^\s*(.+?)\s*$#m' + ), array( + ' ', + '$1' + ), str_replace("\n", '', $pgs[$i])); + + // wordwraps + indents lines + $pgs[$i] = str_repeat($this->indChar, $this->indFirst) . wordwrap($pgs[$i], max($this->wrap - $this->indent, 1), $this->wrapChar . str_repeat($this->indChar, $this->indent), $this->wrapCut); + } + + if ($this->assign !== '') { + $this->core->assignInScope(implode($this->wrapChar . $this->wrapChar, $pgs), $this->assign); + } else { + return implode($this->wrapChar . $this->wrapChar, $pgs); + } + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginTopLevelBlock.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginTopLevelBlock.php new file mode 100644 index 0000000..2694be2 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginTopLevelBlock.php @@ -0,0 +1,60 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Internal plugin used to wrap the template output, do not use in your templates as it will break them. + * This software is provided 'as-is', without any express or implied warranty. + */ +final class PluginTopLevelBlock extends BlockPlugin implements ICompilableBlock +{ + public function init() + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return '/* end template head */ ob_start(); /* template body */ ' . Compiler::PHP_CLOSE; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + return $content . Compiler::PHP_OPEN . ' /* end template body */' . "\n" . 'return $this->buffer . ob_get_clean();'; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginWith.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginWith.php new file mode 100644 index 0000000..f5e94fc --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginWith.php @@ -0,0 +1,99 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\IElseable; +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * Moves the scope down into the provided variable, allowing you to use shorter + * variable names if you repeatedly access values into a single array. + * The with block won't display anything at all if the provided scope is empty, + * so in effect it acts as {if $var}*content*{/if} + *
    + *  * var : the variable name to move into
    + * 
    + * Example : + * instead of the following : + * + * {if $long.boring.prefix} + * {$long.boring.prefix.val} - {$long.boring.prefix.secondVal} - {$long.boring.prefix.thirdVal} + * {/if} + * + * you can use : + * + * {with $long.boring.prefix} + * {$val} - {$secondVal} - {$thirdVal} + * {/with} + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginWith extends BlockPlugin implements ICompilableBlock, IElseable +{ + protected static $cnt = 0; + + /** + * @param $var + */ + public function init($var) + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + $rparams = $compiler->getRealParams($params); + $cparams = $compiler->getCompiledParams($params); + + $compiler->setScope($rparams['var']); + + $pre = Compiler::PHP_OPEN . 'if (' . $cparams['var'] . ')' . "\n{\n" . '$_with' . (self::$cnt) . ' = $this->setScope("' . $rparams['var'] . '");' . "\n/* -- start with output */\n" . Compiler::PHP_CLOSE; + + $post = Compiler::PHP_OPEN . "\n/* -- end with output */\n" . '$this->setScope($_with' . (self::$cnt ++) . ', true);' . "\n}\n" . Compiler::PHP_CLOSE; + + if (isset($params['hasElse'])) { + $post .= $params['hasElse']; + } + + return $pre . $content . $post; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginWithelse.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginWithelse.php new file mode 100644 index 0000000..a614322 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Blocks/PluginWithelse.php @@ -0,0 +1,73 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-19 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Blocks; + +use Dwoo\Compiler; +use Dwoo\Block\Plugin as BlockPlugin; +use Dwoo\ICompilable\Block as ICompilableBlock; + +/** + * This plugin serves as a {else} block specifically for the {with} plugin. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginWithelse extends BlockPlugin implements ICompilableBlock +{ + public function init() + { + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $type + * + * @return string + */ + public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type) + { + $with = &$compiler->findBlock('with', true); + + $params['initialized'] = true; + $compiler->injectBlock($type, $params); + + return ''; + } + + /** + * @param Compiler $compiler + * @param array $params + * @param string $prepend + * @param string $append + * @param string $content + * + * @return string + */ + public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) + { + if (!isset($params['initialized'])) { + return ''; + } + + $block = &$compiler->getCurrentBlock(); + $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE; + + return ''; + } +} diff --git a/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Filters/PluginHtmlFormat.php b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Filters/PluginHtmlFormat.php new file mode 100644 index 0000000..f923de4 --- /dev/null +++ b/system/templateEngines/Dwoo/dwoo/dwoo/lib/Dwoo/Plugins/Filters/PluginHtmlFormat.php @@ -0,0 +1,190 @@ + + * @author David Sanchez + * @copyright 2008-2013 Jordi Boggiano + * @copyright 2013-2016 David Sanchez + * @license http://dwoo.org/LICENSE Modified BSD License + * @version 1.3.0 + * @date 2016-09-18 + * @link http://dwoo.org/ + */ + +namespace Dwoo\Plugins\Filters; + +use Dwoo\Filter; + +/** + * Formats any html output (must be valid xml where every tag opened is closed) + * using a single tab for indenting. 'pre' and other whitespace sensitive + * tags should not be affected. + * It is not recommended to use this on every template if you render multiple + * templates per page, you should only use it once on the main page template so that + * everything is formatted in one pass. + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + */ +class PluginHtmlFormat extends Filter +{ + /** + * tab count to auto-indent the source. + * + * @var int + */ + protected static $tabCount = - 1; + + /** + * stores the additional data (following a tag) of the last call to open/close/singleTag. + * + * @var string + */ + protected static $lastCallAdd = ''; + + /** + * formats the input using the singleTag/closeTag/openTag functions. + * It is auto indenting the whole code, excluding