├── .github └── workflows │ ├── docs.yml │ └── php.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── doc ├── config.toml ├── content │ ├── _index.md │ ├── engine │ │ ├── _index.md │ │ ├── extensions.md │ │ ├── file-extensions.md │ │ ├── folders.md │ │ ├── functions.md │ │ ├── overview.md │ │ └── themes.md │ ├── extensions │ │ ├── _index.md │ │ ├── asset.md │ │ ├── community.md │ │ └── uri.md │ ├── getting-started │ │ ├── _index.md │ │ ├── installation.md │ │ └── simple-example.md │ └── templates │ │ ├── _index.md │ │ ├── data.md │ │ ├── escaping.md │ │ ├── functions.md │ │ ├── inheritance.md │ │ ├── layouts.md │ │ ├── nesting.md │ │ ├── overview.md │ │ ├── sections.md │ │ └── syntax.md ├── layouts │ ├── _default │ │ ├── baseof.html │ │ ├── list.html │ │ └── single.html │ ├── partials │ │ └── title.html │ └── shortcodes │ │ ├── code-filename.html │ │ └── html.html └── static │ ├── CNAME │ ├── css │ └── custom.css │ ├── favicon │ ├── apple-touch-icon-precomposed.png │ └── favicon.ico │ └── images │ └── logo.png ├── example ├── example.php └── templates │ ├── layout.php │ ├── profile.php │ └── sidebar.php ├── phpunit.xml.dist └── src ├── Engine.php ├── Exception └── TemplateNotFound.php ├── Extension ├── Asset.php ├── ExtensionInterface.php └── URI.php └── Template ├── Data.php ├── Directory.php ├── FileExtension.php ├── Folder.php ├── Folders.php ├── Func.php ├── Functions.php ├── Name.php ├── ResolveTemplatePath.php ├── ResolveTemplatePath ├── NameAndFolderResolveTemplatePath.php └── ThemeResolveTemplatePath.php ├── Template.php └── Theme.php /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - v3 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Setup Hugo 15 | uses: peaceiris/actions-hugo@v3 16 | with: 17 | hugo-version: "0.136.5" 18 | 19 | - name: Build 20 | run: hugo --minify -s doc 21 | 22 | - name: Deploy 23 | uses: peaceiris/actions-gh-pages@v4 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./doc/public 27 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP 2 | 3 | on: [push] 4 | 5 | jobs: 6 | run: 7 | runs-on: "ubuntu-latest" 8 | strategy: 9 | matrix: 10 | php-versions: ["8.2", "8.3", "8.4"] 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | 15 | - name: Setup PHP 16 | uses: shivammathur/setup-php@v2 17 | with: 18 | php-version: ${{ matrix.php-versions }} 19 | extensions: mbstring, intl 20 | ini-values: post_max_size=256M, max_execution_time=180 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }} 23 | 24 | - name: Validate Composer 25 | run: composer validate 26 | - name: Install Composer Deps 27 | run: composer install 28 | - name: Run Tests 29 | run: composer test 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/plates). 6 | 7 | ## Pull Requests 8 | 9 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 10 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 11 | - **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date. 12 | - **Consider our release cycle** - We try to follow semver. Randomly breaking public APIs is not an option. 13 | - **Create topic branches** - Don't ask us to pull from your master branch. 14 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 15 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. 16 | 17 | ## Running Tests 18 | 19 | ``` bash 20 | $ phpunit 21 | ``` 22 | 23 | ## Docs 24 | 25 | Docs are served with hugo running on version 0.79 or later. 26 | 27 | You can view the docs locally with `hugo -s doc server` 28 | 29 | **Happy coding**! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 The League of Extraordinary Packages 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Plates 2 | ====== 3 | 4 | [![Maintainer](http://img.shields.io/badge/maintainer-@ragboyjr-blue.svg?style=flat-square)](https://twitter.com/ragboyjr) 5 | [![Source Code](http://img.shields.io/badge/source-league/plates-blue.svg?style=flat-square)](https://github.com/thephpleague/plates) 6 | [![Latest Version](https://img.shields.io/github/release/thephpleague/plates.svg?style=flat-square)](https://github.com/thephpleague/plates/releases) 7 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 8 | [![Build Status](https://img.shields.io/github/actions/workflow/status/thephpleague/plates/php.yml?style=flat-square)](https://github.com/thephpleague/plates/actions?query=workflow%3APHP+branch%3Av3) 9 | [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/plates.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/plates) 10 | [![Total Downloads](https://img.shields.io/packagist/dt/league/plates.svg?style=flat-square)](https://packagist.org/packages/league/plates) 11 | 12 | Plates is a native PHP template system that's fast, easy to use and easy to extend. It's inspired by the excellent [Twig](http://twig.sensiolabs.org/) template engine and strives to bring modern template language functionality to native PHP templates. Plates is designed for developers who prefer to use native PHP templates over compiled template languages, such as Twig or Smarty. 13 | 14 | ### Highlights 15 | 16 | - Native PHP templates, no new [syntax](https://platesphp.com/templates/syntax/) to learn 17 | - Plates is a template system, not a template language 18 | - Plates encourages the use of existing PHP functions 19 | - Increase code reuse with template [layouts](https://platesphp.com/templates/layouts/) and [inheritance](https://platesphp.com/templates/inheritance/) 20 | - Template [folders](https://platesphp.com/engine/folders/) for grouping templates into namespaces 21 | - [Data](https://platesphp.com/templates/data/#preassigned-and-shared-data) sharing across templates 22 | - Preassign [data](https://platesphp.com/templates/data/#preassigned-and-shared-data) to specific templates 23 | - Built-in [escaping](https://platesphp.com/templates/escaping/) helpers 24 | - Easy to extend using [functions](https://platesphp.com/engine/functions/) and [extensions](https://platesphp.com/engine/extensions/) 25 | - Framework-agnostic, will work with any project 26 | - Decoupled design makes templates easy to test 27 | - Composer ready and PSR-2 compliant 28 | 29 | ## Installation 30 | 31 | Plates is available via Composer: 32 | 33 | ``` 34 | composer require league/plates 35 | ``` 36 | 37 | ## Documentation 38 | 39 | Full documentation can be found at [platesphp.com](https://platesphp.com/). 40 | 41 | ## Testing 42 | 43 | ```bash 44 | composer test 45 | ``` 46 | 47 | ## Contributing 48 | 49 | Please see [CONTRIBUTING](https://github.com/thephpleague/plates/blob/master/CONTRIBUTING.md) for details. 50 | 51 | ## Security 52 | 53 | If you discover any security related issues, please email ragboyjr@icloud.com instead of using the issue tracker. 54 | 55 | ## Credits 56 | 57 | - [RJ Garcia](https://github.com/ragboyjr) (Current Maintainer) 58 | - [Jonathan Reinink](https://github.com/reinink) (Original Author) 59 | - [All Contributors](https://github.com/thephpleague/plates/contributors) 60 | 61 | ## License 62 | 63 | The MIT License (MIT). Please see [License File](https://github.com/thephpleague/plates/blob/master/LICENSE) for more information. 64 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "league/plates", 3 | "description": "Plates, the native PHP template system that's fast, easy to use and easy to extend.", 4 | "keywords": [ 5 | "league", 6 | "package", 7 | "templating", 8 | "templates", 9 | "views" 10 | ], 11 | "homepage": "https://platesphp.com", 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Jonathan Reinink", 16 | "email": "jonathan@reinink.ca", 17 | "role": "Developer" 18 | }, 19 | { 20 | "name": "RJ Garcia", 21 | "email": "ragboyjr@icloud.com", 22 | "role": "Developer" 23 | } 24 | ], 25 | "require": { 26 | "php": "^8.0" 27 | }, 28 | "require-dev": { 29 | "mikey179/vfsstream": "^1.6", 30 | "phpunit/phpunit": "^11.4", 31 | "squizlabs/php_codesniffer": "^3.5" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "League\\Plates\\": "src" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "League\\Plates\\Tests\\": "tests" 41 | } 42 | }, 43 | "extra": { 44 | "branch-alias": { 45 | "dev-master": "3.0-dev" 46 | } 47 | }, 48 | "scripts": { 49 | "test": "phpunit --testdox --colors=always", 50 | "docs": "hugo -s doc server" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "620429020e00047ebb1444b0f6ea0a63", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "mikey179/vfsstream", 12 | "version": "v1.6.12", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/bovigo/vfsStream.git", 16 | "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/fe695ec993e0a55c3abdda10a9364eb31c6f1bf0", 21 | "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=7.1.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^7.5||^8.5||^9.6", 29 | "yoast/phpunit-polyfills": "^2.0" 30 | }, 31 | "type": "library", 32 | "extra": { 33 | "branch-alias": { 34 | "dev-master": "1.6.x-dev" 35 | } 36 | }, 37 | "autoload": { 38 | "psr-0": { 39 | "org\\bovigo\\vfs\\": "src/main/php" 40 | } 41 | }, 42 | "notification-url": "https://packagist.org/downloads/", 43 | "license": [ 44 | "BSD-3-Clause" 45 | ], 46 | "authors": [ 47 | { 48 | "name": "Frank Kleine", 49 | "homepage": "http://frankkleine.de/", 50 | "role": "Developer" 51 | } 52 | ], 53 | "description": "Virtual file system to mock the real file system in unit tests.", 54 | "homepage": "http://vfs.bovigo.org/", 55 | "support": { 56 | "issues": "https://github.com/bovigo/vfsStream/issues", 57 | "source": "https://github.com/bovigo/vfsStream/tree/master", 58 | "wiki": "https://github.com/bovigo/vfsStream/wiki" 59 | }, 60 | "time": "2024-08-29T18:43:31+00:00" 61 | }, 62 | { 63 | "name": "myclabs/deep-copy", 64 | "version": "1.12.0", 65 | "source": { 66 | "type": "git", 67 | "url": "https://github.com/myclabs/DeepCopy.git", 68 | "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" 69 | }, 70 | "dist": { 71 | "type": "zip", 72 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", 73 | "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", 74 | "shasum": "" 75 | }, 76 | "require": { 77 | "php": "^7.1 || ^8.0" 78 | }, 79 | "conflict": { 80 | "doctrine/collections": "<1.6.8", 81 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 82 | }, 83 | "require-dev": { 84 | "doctrine/collections": "^1.6.8", 85 | "doctrine/common": "^2.13.3 || ^3.2.2", 86 | "phpspec/prophecy": "^1.10", 87 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 88 | }, 89 | "type": "library", 90 | "autoload": { 91 | "files": [ 92 | "src/DeepCopy/deep_copy.php" 93 | ], 94 | "psr-4": { 95 | "DeepCopy\\": "src/DeepCopy/" 96 | } 97 | }, 98 | "notification-url": "https://packagist.org/downloads/", 99 | "license": [ 100 | "MIT" 101 | ], 102 | "description": "Create deep copies (clones) of your objects", 103 | "keywords": [ 104 | "clone", 105 | "copy", 106 | "duplicate", 107 | "object", 108 | "object graph" 109 | ], 110 | "support": { 111 | "issues": "https://github.com/myclabs/DeepCopy/issues", 112 | "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" 113 | }, 114 | "funding": [ 115 | { 116 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 117 | "type": "tidelift" 118 | } 119 | ], 120 | "time": "2024-06-12T14:39:25+00:00" 121 | }, 122 | { 123 | "name": "nikic/php-parser", 124 | "version": "v5.3.1", 125 | "source": { 126 | "type": "git", 127 | "url": "https://github.com/nikic/PHP-Parser.git", 128 | "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" 129 | }, 130 | "dist": { 131 | "type": "zip", 132 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", 133 | "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", 134 | "shasum": "" 135 | }, 136 | "require": { 137 | "ext-ctype": "*", 138 | "ext-json": "*", 139 | "ext-tokenizer": "*", 140 | "php": ">=7.4" 141 | }, 142 | "require-dev": { 143 | "ircmaxell/php-yacc": "^0.0.7", 144 | "phpunit/phpunit": "^9.0" 145 | }, 146 | "bin": [ 147 | "bin/php-parse" 148 | ], 149 | "type": "library", 150 | "extra": { 151 | "branch-alias": { 152 | "dev-master": "5.0-dev" 153 | } 154 | }, 155 | "autoload": { 156 | "psr-4": { 157 | "PhpParser\\": "lib/PhpParser" 158 | } 159 | }, 160 | "notification-url": "https://packagist.org/downloads/", 161 | "license": [ 162 | "BSD-3-Clause" 163 | ], 164 | "authors": [ 165 | { 166 | "name": "Nikita Popov" 167 | } 168 | ], 169 | "description": "A PHP parser written in PHP", 170 | "keywords": [ 171 | "parser", 172 | "php" 173 | ], 174 | "support": { 175 | "issues": "https://github.com/nikic/PHP-Parser/issues", 176 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" 177 | }, 178 | "time": "2024-10-08T18:51:32+00:00" 179 | }, 180 | { 181 | "name": "phar-io/manifest", 182 | "version": "2.0.4", 183 | "source": { 184 | "type": "git", 185 | "url": "https://github.com/phar-io/manifest.git", 186 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 187 | }, 188 | "dist": { 189 | "type": "zip", 190 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 191 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 192 | "shasum": "" 193 | }, 194 | "require": { 195 | "ext-dom": "*", 196 | "ext-libxml": "*", 197 | "ext-phar": "*", 198 | "ext-xmlwriter": "*", 199 | "phar-io/version": "^3.0.1", 200 | "php": "^7.2 || ^8.0" 201 | }, 202 | "type": "library", 203 | "extra": { 204 | "branch-alias": { 205 | "dev-master": "2.0.x-dev" 206 | } 207 | }, 208 | "autoload": { 209 | "classmap": [ 210 | "src/" 211 | ] 212 | }, 213 | "notification-url": "https://packagist.org/downloads/", 214 | "license": [ 215 | "BSD-3-Clause" 216 | ], 217 | "authors": [ 218 | { 219 | "name": "Arne Blankerts", 220 | "email": "arne@blankerts.de", 221 | "role": "Developer" 222 | }, 223 | { 224 | "name": "Sebastian Heuer", 225 | "email": "sebastian@phpeople.de", 226 | "role": "Developer" 227 | }, 228 | { 229 | "name": "Sebastian Bergmann", 230 | "email": "sebastian@phpunit.de", 231 | "role": "Developer" 232 | } 233 | ], 234 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 235 | "support": { 236 | "issues": "https://github.com/phar-io/manifest/issues", 237 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 238 | }, 239 | "funding": [ 240 | { 241 | "url": "https://github.com/theseer", 242 | "type": "github" 243 | } 244 | ], 245 | "time": "2024-03-03T12:33:53+00:00" 246 | }, 247 | { 248 | "name": "phar-io/version", 249 | "version": "3.2.1", 250 | "source": { 251 | "type": "git", 252 | "url": "https://github.com/phar-io/version.git", 253 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 254 | }, 255 | "dist": { 256 | "type": "zip", 257 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 258 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 259 | "shasum": "" 260 | }, 261 | "require": { 262 | "php": "^7.2 || ^8.0" 263 | }, 264 | "type": "library", 265 | "autoload": { 266 | "classmap": [ 267 | "src/" 268 | ] 269 | }, 270 | "notification-url": "https://packagist.org/downloads/", 271 | "license": [ 272 | "BSD-3-Clause" 273 | ], 274 | "authors": [ 275 | { 276 | "name": "Arne Blankerts", 277 | "email": "arne@blankerts.de", 278 | "role": "Developer" 279 | }, 280 | { 281 | "name": "Sebastian Heuer", 282 | "email": "sebastian@phpeople.de", 283 | "role": "Developer" 284 | }, 285 | { 286 | "name": "Sebastian Bergmann", 287 | "email": "sebastian@phpunit.de", 288 | "role": "Developer" 289 | } 290 | ], 291 | "description": "Library for handling version information and constraints", 292 | "support": { 293 | "issues": "https://github.com/phar-io/version/issues", 294 | "source": "https://github.com/phar-io/version/tree/3.2.1" 295 | }, 296 | "time": "2022-02-21T01:04:05+00:00" 297 | }, 298 | { 299 | "name": "phpunit/php-code-coverage", 300 | "version": "11.0.7", 301 | "source": { 302 | "type": "git", 303 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 304 | "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca" 305 | }, 306 | "dist": { 307 | "type": "zip", 308 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca", 309 | "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca", 310 | "shasum": "" 311 | }, 312 | "require": { 313 | "ext-dom": "*", 314 | "ext-libxml": "*", 315 | "ext-xmlwriter": "*", 316 | "nikic/php-parser": "^5.3.1", 317 | "php": ">=8.2", 318 | "phpunit/php-file-iterator": "^5.1.0", 319 | "phpunit/php-text-template": "^4.0.1", 320 | "sebastian/code-unit-reverse-lookup": "^4.0.1", 321 | "sebastian/complexity": "^4.0.1", 322 | "sebastian/environment": "^7.2.0", 323 | "sebastian/lines-of-code": "^3.0.1", 324 | "sebastian/version": "^5.0.2", 325 | "theseer/tokenizer": "^1.2.3" 326 | }, 327 | "require-dev": { 328 | "phpunit/phpunit": "^11.4.1" 329 | }, 330 | "suggest": { 331 | "ext-pcov": "PHP extension that provides line coverage", 332 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 333 | }, 334 | "type": "library", 335 | "extra": { 336 | "branch-alias": { 337 | "dev-main": "11.0.x-dev" 338 | } 339 | }, 340 | "autoload": { 341 | "classmap": [ 342 | "src/" 343 | ] 344 | }, 345 | "notification-url": "https://packagist.org/downloads/", 346 | "license": [ 347 | "BSD-3-Clause" 348 | ], 349 | "authors": [ 350 | { 351 | "name": "Sebastian Bergmann", 352 | "email": "sebastian@phpunit.de", 353 | "role": "lead" 354 | } 355 | ], 356 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 357 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 358 | "keywords": [ 359 | "coverage", 360 | "testing", 361 | "xunit" 362 | ], 363 | "support": { 364 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 365 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 366 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7" 367 | }, 368 | "funding": [ 369 | { 370 | "url": "https://github.com/sebastianbergmann", 371 | "type": "github" 372 | } 373 | ], 374 | "time": "2024-10-09T06:21:38+00:00" 375 | }, 376 | { 377 | "name": "phpunit/php-file-iterator", 378 | "version": "5.1.0", 379 | "source": { 380 | "type": "git", 381 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 382 | "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" 383 | }, 384 | "dist": { 385 | "type": "zip", 386 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", 387 | "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", 388 | "shasum": "" 389 | }, 390 | "require": { 391 | "php": ">=8.2" 392 | }, 393 | "require-dev": { 394 | "phpunit/phpunit": "^11.0" 395 | }, 396 | "type": "library", 397 | "extra": { 398 | "branch-alias": { 399 | "dev-main": "5.0-dev" 400 | } 401 | }, 402 | "autoload": { 403 | "classmap": [ 404 | "src/" 405 | ] 406 | }, 407 | "notification-url": "https://packagist.org/downloads/", 408 | "license": [ 409 | "BSD-3-Clause" 410 | ], 411 | "authors": [ 412 | { 413 | "name": "Sebastian Bergmann", 414 | "email": "sebastian@phpunit.de", 415 | "role": "lead" 416 | } 417 | ], 418 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 419 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 420 | "keywords": [ 421 | "filesystem", 422 | "iterator" 423 | ], 424 | "support": { 425 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 426 | "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", 427 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" 428 | }, 429 | "funding": [ 430 | { 431 | "url": "https://github.com/sebastianbergmann", 432 | "type": "github" 433 | } 434 | ], 435 | "time": "2024-08-27T05:02:59+00:00" 436 | }, 437 | { 438 | "name": "phpunit/php-invoker", 439 | "version": "5.0.1", 440 | "source": { 441 | "type": "git", 442 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 443 | "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" 444 | }, 445 | "dist": { 446 | "type": "zip", 447 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", 448 | "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", 449 | "shasum": "" 450 | }, 451 | "require": { 452 | "php": ">=8.2" 453 | }, 454 | "require-dev": { 455 | "ext-pcntl": "*", 456 | "phpunit/phpunit": "^11.0" 457 | }, 458 | "suggest": { 459 | "ext-pcntl": "*" 460 | }, 461 | "type": "library", 462 | "extra": { 463 | "branch-alias": { 464 | "dev-main": "5.0-dev" 465 | } 466 | }, 467 | "autoload": { 468 | "classmap": [ 469 | "src/" 470 | ] 471 | }, 472 | "notification-url": "https://packagist.org/downloads/", 473 | "license": [ 474 | "BSD-3-Clause" 475 | ], 476 | "authors": [ 477 | { 478 | "name": "Sebastian Bergmann", 479 | "email": "sebastian@phpunit.de", 480 | "role": "lead" 481 | } 482 | ], 483 | "description": "Invoke callables with a timeout", 484 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 485 | "keywords": [ 486 | "process" 487 | ], 488 | "support": { 489 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 490 | "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", 491 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" 492 | }, 493 | "funding": [ 494 | { 495 | "url": "https://github.com/sebastianbergmann", 496 | "type": "github" 497 | } 498 | ], 499 | "time": "2024-07-03T05:07:44+00:00" 500 | }, 501 | { 502 | "name": "phpunit/php-text-template", 503 | "version": "4.0.1", 504 | "source": { 505 | "type": "git", 506 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 507 | "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" 508 | }, 509 | "dist": { 510 | "type": "zip", 511 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", 512 | "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", 513 | "shasum": "" 514 | }, 515 | "require": { 516 | "php": ">=8.2" 517 | }, 518 | "require-dev": { 519 | "phpunit/phpunit": "^11.0" 520 | }, 521 | "type": "library", 522 | "extra": { 523 | "branch-alias": { 524 | "dev-main": "4.0-dev" 525 | } 526 | }, 527 | "autoload": { 528 | "classmap": [ 529 | "src/" 530 | ] 531 | }, 532 | "notification-url": "https://packagist.org/downloads/", 533 | "license": [ 534 | "BSD-3-Clause" 535 | ], 536 | "authors": [ 537 | { 538 | "name": "Sebastian Bergmann", 539 | "email": "sebastian@phpunit.de", 540 | "role": "lead" 541 | } 542 | ], 543 | "description": "Simple template engine.", 544 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 545 | "keywords": [ 546 | "template" 547 | ], 548 | "support": { 549 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 550 | "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", 551 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" 552 | }, 553 | "funding": [ 554 | { 555 | "url": "https://github.com/sebastianbergmann", 556 | "type": "github" 557 | } 558 | ], 559 | "time": "2024-07-03T05:08:43+00:00" 560 | }, 561 | { 562 | "name": "phpunit/php-timer", 563 | "version": "7.0.1", 564 | "source": { 565 | "type": "git", 566 | "url": "https://github.com/sebastianbergmann/php-timer.git", 567 | "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" 568 | }, 569 | "dist": { 570 | "type": "zip", 571 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", 572 | "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", 573 | "shasum": "" 574 | }, 575 | "require": { 576 | "php": ">=8.2" 577 | }, 578 | "require-dev": { 579 | "phpunit/phpunit": "^11.0" 580 | }, 581 | "type": "library", 582 | "extra": { 583 | "branch-alias": { 584 | "dev-main": "7.0-dev" 585 | } 586 | }, 587 | "autoload": { 588 | "classmap": [ 589 | "src/" 590 | ] 591 | }, 592 | "notification-url": "https://packagist.org/downloads/", 593 | "license": [ 594 | "BSD-3-Clause" 595 | ], 596 | "authors": [ 597 | { 598 | "name": "Sebastian Bergmann", 599 | "email": "sebastian@phpunit.de", 600 | "role": "lead" 601 | } 602 | ], 603 | "description": "Utility class for timing", 604 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 605 | "keywords": [ 606 | "timer" 607 | ], 608 | "support": { 609 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 610 | "security": "https://github.com/sebastianbergmann/php-timer/security/policy", 611 | "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" 612 | }, 613 | "funding": [ 614 | { 615 | "url": "https://github.com/sebastianbergmann", 616 | "type": "github" 617 | } 618 | ], 619 | "time": "2024-07-03T05:09:35+00:00" 620 | }, 621 | { 622 | "name": "phpunit/phpunit", 623 | "version": "11.4.3", 624 | "source": { 625 | "type": "git", 626 | "url": "https://github.com/sebastianbergmann/phpunit.git", 627 | "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76" 628 | }, 629 | "dist": { 630 | "type": "zip", 631 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e8e8ed1854de5d36c088ec1833beae40d2dedd76", 632 | "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76", 633 | "shasum": "" 634 | }, 635 | "require": { 636 | "ext-dom": "*", 637 | "ext-json": "*", 638 | "ext-libxml": "*", 639 | "ext-mbstring": "*", 640 | "ext-xml": "*", 641 | "ext-xmlwriter": "*", 642 | "myclabs/deep-copy": "^1.12.0", 643 | "phar-io/manifest": "^2.0.4", 644 | "phar-io/version": "^3.2.1", 645 | "php": ">=8.2", 646 | "phpunit/php-code-coverage": "^11.0.7", 647 | "phpunit/php-file-iterator": "^5.1.0", 648 | "phpunit/php-invoker": "^5.0.1", 649 | "phpunit/php-text-template": "^4.0.1", 650 | "phpunit/php-timer": "^7.0.1", 651 | "sebastian/cli-parser": "^3.0.2", 652 | "sebastian/code-unit": "^3.0.1", 653 | "sebastian/comparator": "^6.1.1", 654 | "sebastian/diff": "^6.0.2", 655 | "sebastian/environment": "^7.2.0", 656 | "sebastian/exporter": "^6.1.3", 657 | "sebastian/global-state": "^7.0.2", 658 | "sebastian/object-enumerator": "^6.0.1", 659 | "sebastian/type": "^5.1.0", 660 | "sebastian/version": "^5.0.2" 661 | }, 662 | "suggest": { 663 | "ext-soap": "To be able to generate mocks based on WSDL files" 664 | }, 665 | "bin": [ 666 | "phpunit" 667 | ], 668 | "type": "library", 669 | "extra": { 670 | "branch-alias": { 671 | "dev-main": "11.4-dev" 672 | } 673 | }, 674 | "autoload": { 675 | "files": [ 676 | "src/Framework/Assert/Functions.php" 677 | ], 678 | "classmap": [ 679 | "src/" 680 | ] 681 | }, 682 | "notification-url": "https://packagist.org/downloads/", 683 | "license": [ 684 | "BSD-3-Clause" 685 | ], 686 | "authors": [ 687 | { 688 | "name": "Sebastian Bergmann", 689 | "email": "sebastian@phpunit.de", 690 | "role": "lead" 691 | } 692 | ], 693 | "description": "The PHP Unit Testing framework.", 694 | "homepage": "https://phpunit.de/", 695 | "keywords": [ 696 | "phpunit", 697 | "testing", 698 | "xunit" 699 | ], 700 | "support": { 701 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 702 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 703 | "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.3" 704 | }, 705 | "funding": [ 706 | { 707 | "url": "https://phpunit.de/sponsors.html", 708 | "type": "custom" 709 | }, 710 | { 711 | "url": "https://github.com/sebastianbergmann", 712 | "type": "github" 713 | }, 714 | { 715 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 716 | "type": "tidelift" 717 | } 718 | ], 719 | "time": "2024-10-28T13:07:50+00:00" 720 | }, 721 | { 722 | "name": "sebastian/cli-parser", 723 | "version": "3.0.2", 724 | "source": { 725 | "type": "git", 726 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 727 | "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" 728 | }, 729 | "dist": { 730 | "type": "zip", 731 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", 732 | "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", 733 | "shasum": "" 734 | }, 735 | "require": { 736 | "php": ">=8.2" 737 | }, 738 | "require-dev": { 739 | "phpunit/phpunit": "^11.0" 740 | }, 741 | "type": "library", 742 | "extra": { 743 | "branch-alias": { 744 | "dev-main": "3.0-dev" 745 | } 746 | }, 747 | "autoload": { 748 | "classmap": [ 749 | "src/" 750 | ] 751 | }, 752 | "notification-url": "https://packagist.org/downloads/", 753 | "license": [ 754 | "BSD-3-Clause" 755 | ], 756 | "authors": [ 757 | { 758 | "name": "Sebastian Bergmann", 759 | "email": "sebastian@phpunit.de", 760 | "role": "lead" 761 | } 762 | ], 763 | "description": "Library for parsing CLI options", 764 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 765 | "support": { 766 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 767 | "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", 768 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" 769 | }, 770 | "funding": [ 771 | { 772 | "url": "https://github.com/sebastianbergmann", 773 | "type": "github" 774 | } 775 | ], 776 | "time": "2024-07-03T04:41:36+00:00" 777 | }, 778 | { 779 | "name": "sebastian/code-unit", 780 | "version": "3.0.1", 781 | "source": { 782 | "type": "git", 783 | "url": "https://github.com/sebastianbergmann/code-unit.git", 784 | "reference": "6bb7d09d6623567178cf54126afa9c2310114268" 785 | }, 786 | "dist": { 787 | "type": "zip", 788 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6bb7d09d6623567178cf54126afa9c2310114268", 789 | "reference": "6bb7d09d6623567178cf54126afa9c2310114268", 790 | "shasum": "" 791 | }, 792 | "require": { 793 | "php": ">=8.2" 794 | }, 795 | "require-dev": { 796 | "phpunit/phpunit": "^11.0" 797 | }, 798 | "type": "library", 799 | "extra": { 800 | "branch-alias": { 801 | "dev-main": "3.0-dev" 802 | } 803 | }, 804 | "autoload": { 805 | "classmap": [ 806 | "src/" 807 | ] 808 | }, 809 | "notification-url": "https://packagist.org/downloads/", 810 | "license": [ 811 | "BSD-3-Clause" 812 | ], 813 | "authors": [ 814 | { 815 | "name": "Sebastian Bergmann", 816 | "email": "sebastian@phpunit.de", 817 | "role": "lead" 818 | } 819 | ], 820 | "description": "Collection of value objects that represent the PHP code units", 821 | "homepage": "https://github.com/sebastianbergmann/code-unit", 822 | "support": { 823 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 824 | "security": "https://github.com/sebastianbergmann/code-unit/security/policy", 825 | "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.1" 826 | }, 827 | "funding": [ 828 | { 829 | "url": "https://github.com/sebastianbergmann", 830 | "type": "github" 831 | } 832 | ], 833 | "time": "2024-07-03T04:44:28+00:00" 834 | }, 835 | { 836 | "name": "sebastian/code-unit-reverse-lookup", 837 | "version": "4.0.1", 838 | "source": { 839 | "type": "git", 840 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 841 | "reference": "183a9b2632194febd219bb9246eee421dad8d45e" 842 | }, 843 | "dist": { 844 | "type": "zip", 845 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", 846 | "reference": "183a9b2632194febd219bb9246eee421dad8d45e", 847 | "shasum": "" 848 | }, 849 | "require": { 850 | "php": ">=8.2" 851 | }, 852 | "require-dev": { 853 | "phpunit/phpunit": "^11.0" 854 | }, 855 | "type": "library", 856 | "extra": { 857 | "branch-alias": { 858 | "dev-main": "4.0-dev" 859 | } 860 | }, 861 | "autoload": { 862 | "classmap": [ 863 | "src/" 864 | ] 865 | }, 866 | "notification-url": "https://packagist.org/downloads/", 867 | "license": [ 868 | "BSD-3-Clause" 869 | ], 870 | "authors": [ 871 | { 872 | "name": "Sebastian Bergmann", 873 | "email": "sebastian@phpunit.de" 874 | } 875 | ], 876 | "description": "Looks up which function or method a line of code belongs to", 877 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 878 | "support": { 879 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 880 | "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", 881 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" 882 | }, 883 | "funding": [ 884 | { 885 | "url": "https://github.com/sebastianbergmann", 886 | "type": "github" 887 | } 888 | ], 889 | "time": "2024-07-03T04:45:54+00:00" 890 | }, 891 | { 892 | "name": "sebastian/comparator", 893 | "version": "6.2.1", 894 | "source": { 895 | "type": "git", 896 | "url": "https://github.com/sebastianbergmann/comparator.git", 897 | "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" 898 | }, 899 | "dist": { 900 | "type": "zip", 901 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", 902 | "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", 903 | "shasum": "" 904 | }, 905 | "require": { 906 | "ext-dom": "*", 907 | "ext-mbstring": "*", 908 | "php": ">=8.2", 909 | "sebastian/diff": "^6.0", 910 | "sebastian/exporter": "^6.0" 911 | }, 912 | "require-dev": { 913 | "phpunit/phpunit": "^11.4" 914 | }, 915 | "type": "library", 916 | "extra": { 917 | "branch-alias": { 918 | "dev-main": "6.2-dev" 919 | } 920 | }, 921 | "autoload": { 922 | "classmap": [ 923 | "src/" 924 | ] 925 | }, 926 | "notification-url": "https://packagist.org/downloads/", 927 | "license": [ 928 | "BSD-3-Clause" 929 | ], 930 | "authors": [ 931 | { 932 | "name": "Sebastian Bergmann", 933 | "email": "sebastian@phpunit.de" 934 | }, 935 | { 936 | "name": "Jeff Welch", 937 | "email": "whatthejeff@gmail.com" 938 | }, 939 | { 940 | "name": "Volker Dusch", 941 | "email": "github@wallbash.com" 942 | }, 943 | { 944 | "name": "Bernhard Schussek", 945 | "email": "bschussek@2bepublished.at" 946 | } 947 | ], 948 | "description": "Provides the functionality to compare PHP values for equality", 949 | "homepage": "https://github.com/sebastianbergmann/comparator", 950 | "keywords": [ 951 | "comparator", 952 | "compare", 953 | "equality" 954 | ], 955 | "support": { 956 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 957 | "security": "https://github.com/sebastianbergmann/comparator/security/policy", 958 | "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" 959 | }, 960 | "funding": [ 961 | { 962 | "url": "https://github.com/sebastianbergmann", 963 | "type": "github" 964 | } 965 | ], 966 | "time": "2024-10-31T05:30:08+00:00" 967 | }, 968 | { 969 | "name": "sebastian/complexity", 970 | "version": "4.0.1", 971 | "source": { 972 | "type": "git", 973 | "url": "https://github.com/sebastianbergmann/complexity.git", 974 | "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" 975 | }, 976 | "dist": { 977 | "type": "zip", 978 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", 979 | "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", 980 | "shasum": "" 981 | }, 982 | "require": { 983 | "nikic/php-parser": "^5.0", 984 | "php": ">=8.2" 985 | }, 986 | "require-dev": { 987 | "phpunit/phpunit": "^11.0" 988 | }, 989 | "type": "library", 990 | "extra": { 991 | "branch-alias": { 992 | "dev-main": "4.0-dev" 993 | } 994 | }, 995 | "autoload": { 996 | "classmap": [ 997 | "src/" 998 | ] 999 | }, 1000 | "notification-url": "https://packagist.org/downloads/", 1001 | "license": [ 1002 | "BSD-3-Clause" 1003 | ], 1004 | "authors": [ 1005 | { 1006 | "name": "Sebastian Bergmann", 1007 | "email": "sebastian@phpunit.de", 1008 | "role": "lead" 1009 | } 1010 | ], 1011 | "description": "Library for calculating the complexity of PHP code units", 1012 | "homepage": "https://github.com/sebastianbergmann/complexity", 1013 | "support": { 1014 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1015 | "security": "https://github.com/sebastianbergmann/complexity/security/policy", 1016 | "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" 1017 | }, 1018 | "funding": [ 1019 | { 1020 | "url": "https://github.com/sebastianbergmann", 1021 | "type": "github" 1022 | } 1023 | ], 1024 | "time": "2024-07-03T04:49:50+00:00" 1025 | }, 1026 | { 1027 | "name": "sebastian/diff", 1028 | "version": "6.0.2", 1029 | "source": { 1030 | "type": "git", 1031 | "url": "https://github.com/sebastianbergmann/diff.git", 1032 | "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" 1033 | }, 1034 | "dist": { 1035 | "type": "zip", 1036 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", 1037 | "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", 1038 | "shasum": "" 1039 | }, 1040 | "require": { 1041 | "php": ">=8.2" 1042 | }, 1043 | "require-dev": { 1044 | "phpunit/phpunit": "^11.0", 1045 | "symfony/process": "^4.2 || ^5" 1046 | }, 1047 | "type": "library", 1048 | "extra": { 1049 | "branch-alias": { 1050 | "dev-main": "6.0-dev" 1051 | } 1052 | }, 1053 | "autoload": { 1054 | "classmap": [ 1055 | "src/" 1056 | ] 1057 | }, 1058 | "notification-url": "https://packagist.org/downloads/", 1059 | "license": [ 1060 | "BSD-3-Clause" 1061 | ], 1062 | "authors": [ 1063 | { 1064 | "name": "Sebastian Bergmann", 1065 | "email": "sebastian@phpunit.de" 1066 | }, 1067 | { 1068 | "name": "Kore Nordmann", 1069 | "email": "mail@kore-nordmann.de" 1070 | } 1071 | ], 1072 | "description": "Diff implementation", 1073 | "homepage": "https://github.com/sebastianbergmann/diff", 1074 | "keywords": [ 1075 | "diff", 1076 | "udiff", 1077 | "unidiff", 1078 | "unified diff" 1079 | ], 1080 | "support": { 1081 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1082 | "security": "https://github.com/sebastianbergmann/diff/security/policy", 1083 | "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" 1084 | }, 1085 | "funding": [ 1086 | { 1087 | "url": "https://github.com/sebastianbergmann", 1088 | "type": "github" 1089 | } 1090 | ], 1091 | "time": "2024-07-03T04:53:05+00:00" 1092 | }, 1093 | { 1094 | "name": "sebastian/environment", 1095 | "version": "7.2.0", 1096 | "source": { 1097 | "type": "git", 1098 | "url": "https://github.com/sebastianbergmann/environment.git", 1099 | "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" 1100 | }, 1101 | "dist": { 1102 | "type": "zip", 1103 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", 1104 | "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", 1105 | "shasum": "" 1106 | }, 1107 | "require": { 1108 | "php": ">=8.2" 1109 | }, 1110 | "require-dev": { 1111 | "phpunit/phpunit": "^11.0" 1112 | }, 1113 | "suggest": { 1114 | "ext-posix": "*" 1115 | }, 1116 | "type": "library", 1117 | "extra": { 1118 | "branch-alias": { 1119 | "dev-main": "7.2-dev" 1120 | } 1121 | }, 1122 | "autoload": { 1123 | "classmap": [ 1124 | "src/" 1125 | ] 1126 | }, 1127 | "notification-url": "https://packagist.org/downloads/", 1128 | "license": [ 1129 | "BSD-3-Clause" 1130 | ], 1131 | "authors": [ 1132 | { 1133 | "name": "Sebastian Bergmann", 1134 | "email": "sebastian@phpunit.de" 1135 | } 1136 | ], 1137 | "description": "Provides functionality to handle HHVM/PHP environments", 1138 | "homepage": "https://github.com/sebastianbergmann/environment", 1139 | "keywords": [ 1140 | "Xdebug", 1141 | "environment", 1142 | "hhvm" 1143 | ], 1144 | "support": { 1145 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1146 | "security": "https://github.com/sebastianbergmann/environment/security/policy", 1147 | "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" 1148 | }, 1149 | "funding": [ 1150 | { 1151 | "url": "https://github.com/sebastianbergmann", 1152 | "type": "github" 1153 | } 1154 | ], 1155 | "time": "2024-07-03T04:54:44+00:00" 1156 | }, 1157 | { 1158 | "name": "sebastian/exporter", 1159 | "version": "6.1.3", 1160 | "source": { 1161 | "type": "git", 1162 | "url": "https://github.com/sebastianbergmann/exporter.git", 1163 | "reference": "c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e" 1164 | }, 1165 | "dist": { 1166 | "type": "zip", 1167 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e", 1168 | "reference": "c414673eee9a8f9d51bbf8d61fc9e3ef1e85b20e", 1169 | "shasum": "" 1170 | }, 1171 | "require": { 1172 | "ext-mbstring": "*", 1173 | "php": ">=8.2", 1174 | "sebastian/recursion-context": "^6.0" 1175 | }, 1176 | "require-dev": { 1177 | "phpunit/phpunit": "^11.2" 1178 | }, 1179 | "type": "library", 1180 | "extra": { 1181 | "branch-alias": { 1182 | "dev-main": "6.1-dev" 1183 | } 1184 | }, 1185 | "autoload": { 1186 | "classmap": [ 1187 | "src/" 1188 | ] 1189 | }, 1190 | "notification-url": "https://packagist.org/downloads/", 1191 | "license": [ 1192 | "BSD-3-Clause" 1193 | ], 1194 | "authors": [ 1195 | { 1196 | "name": "Sebastian Bergmann", 1197 | "email": "sebastian@phpunit.de" 1198 | }, 1199 | { 1200 | "name": "Jeff Welch", 1201 | "email": "whatthejeff@gmail.com" 1202 | }, 1203 | { 1204 | "name": "Volker Dusch", 1205 | "email": "github@wallbash.com" 1206 | }, 1207 | { 1208 | "name": "Adam Harvey", 1209 | "email": "aharvey@php.net" 1210 | }, 1211 | { 1212 | "name": "Bernhard Schussek", 1213 | "email": "bschussek@gmail.com" 1214 | } 1215 | ], 1216 | "description": "Provides the functionality to export PHP variables for visualization", 1217 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1218 | "keywords": [ 1219 | "export", 1220 | "exporter" 1221 | ], 1222 | "support": { 1223 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1224 | "security": "https://github.com/sebastianbergmann/exporter/security/policy", 1225 | "source": "https://github.com/sebastianbergmann/exporter/tree/6.1.3" 1226 | }, 1227 | "funding": [ 1228 | { 1229 | "url": "https://github.com/sebastianbergmann", 1230 | "type": "github" 1231 | } 1232 | ], 1233 | "time": "2024-07-03T04:56:19+00:00" 1234 | }, 1235 | { 1236 | "name": "sebastian/global-state", 1237 | "version": "7.0.2", 1238 | "source": { 1239 | "type": "git", 1240 | "url": "https://github.com/sebastianbergmann/global-state.git", 1241 | "reference": "3be331570a721f9a4b5917f4209773de17f747d7" 1242 | }, 1243 | "dist": { 1244 | "type": "zip", 1245 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", 1246 | "reference": "3be331570a721f9a4b5917f4209773de17f747d7", 1247 | "shasum": "" 1248 | }, 1249 | "require": { 1250 | "php": ">=8.2", 1251 | "sebastian/object-reflector": "^4.0", 1252 | "sebastian/recursion-context": "^6.0" 1253 | }, 1254 | "require-dev": { 1255 | "ext-dom": "*", 1256 | "phpunit/phpunit": "^11.0" 1257 | }, 1258 | "type": "library", 1259 | "extra": { 1260 | "branch-alias": { 1261 | "dev-main": "7.0-dev" 1262 | } 1263 | }, 1264 | "autoload": { 1265 | "classmap": [ 1266 | "src/" 1267 | ] 1268 | }, 1269 | "notification-url": "https://packagist.org/downloads/", 1270 | "license": [ 1271 | "BSD-3-Clause" 1272 | ], 1273 | "authors": [ 1274 | { 1275 | "name": "Sebastian Bergmann", 1276 | "email": "sebastian@phpunit.de" 1277 | } 1278 | ], 1279 | "description": "Snapshotting of global state", 1280 | "homepage": "https://www.github.com/sebastianbergmann/global-state", 1281 | "keywords": [ 1282 | "global state" 1283 | ], 1284 | "support": { 1285 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1286 | "security": "https://github.com/sebastianbergmann/global-state/security/policy", 1287 | "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" 1288 | }, 1289 | "funding": [ 1290 | { 1291 | "url": "https://github.com/sebastianbergmann", 1292 | "type": "github" 1293 | } 1294 | ], 1295 | "time": "2024-07-03T04:57:36+00:00" 1296 | }, 1297 | { 1298 | "name": "sebastian/lines-of-code", 1299 | "version": "3.0.1", 1300 | "source": { 1301 | "type": "git", 1302 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1303 | "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" 1304 | }, 1305 | "dist": { 1306 | "type": "zip", 1307 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", 1308 | "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", 1309 | "shasum": "" 1310 | }, 1311 | "require": { 1312 | "nikic/php-parser": "^5.0", 1313 | "php": ">=8.2" 1314 | }, 1315 | "require-dev": { 1316 | "phpunit/phpunit": "^11.0" 1317 | }, 1318 | "type": "library", 1319 | "extra": { 1320 | "branch-alias": { 1321 | "dev-main": "3.0-dev" 1322 | } 1323 | }, 1324 | "autoload": { 1325 | "classmap": [ 1326 | "src/" 1327 | ] 1328 | }, 1329 | "notification-url": "https://packagist.org/downloads/", 1330 | "license": [ 1331 | "BSD-3-Clause" 1332 | ], 1333 | "authors": [ 1334 | { 1335 | "name": "Sebastian Bergmann", 1336 | "email": "sebastian@phpunit.de", 1337 | "role": "lead" 1338 | } 1339 | ], 1340 | "description": "Library for counting the lines of code in PHP source code", 1341 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1342 | "support": { 1343 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1344 | "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", 1345 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" 1346 | }, 1347 | "funding": [ 1348 | { 1349 | "url": "https://github.com/sebastianbergmann", 1350 | "type": "github" 1351 | } 1352 | ], 1353 | "time": "2024-07-03T04:58:38+00:00" 1354 | }, 1355 | { 1356 | "name": "sebastian/object-enumerator", 1357 | "version": "6.0.1", 1358 | "source": { 1359 | "type": "git", 1360 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1361 | "reference": "f5b498e631a74204185071eb41f33f38d64608aa" 1362 | }, 1363 | "dist": { 1364 | "type": "zip", 1365 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", 1366 | "reference": "f5b498e631a74204185071eb41f33f38d64608aa", 1367 | "shasum": "" 1368 | }, 1369 | "require": { 1370 | "php": ">=8.2", 1371 | "sebastian/object-reflector": "^4.0", 1372 | "sebastian/recursion-context": "^6.0" 1373 | }, 1374 | "require-dev": { 1375 | "phpunit/phpunit": "^11.0" 1376 | }, 1377 | "type": "library", 1378 | "extra": { 1379 | "branch-alias": { 1380 | "dev-main": "6.0-dev" 1381 | } 1382 | }, 1383 | "autoload": { 1384 | "classmap": [ 1385 | "src/" 1386 | ] 1387 | }, 1388 | "notification-url": "https://packagist.org/downloads/", 1389 | "license": [ 1390 | "BSD-3-Clause" 1391 | ], 1392 | "authors": [ 1393 | { 1394 | "name": "Sebastian Bergmann", 1395 | "email": "sebastian@phpunit.de" 1396 | } 1397 | ], 1398 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1399 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1400 | "support": { 1401 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1402 | "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", 1403 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" 1404 | }, 1405 | "funding": [ 1406 | { 1407 | "url": "https://github.com/sebastianbergmann", 1408 | "type": "github" 1409 | } 1410 | ], 1411 | "time": "2024-07-03T05:00:13+00:00" 1412 | }, 1413 | { 1414 | "name": "sebastian/object-reflector", 1415 | "version": "4.0.1", 1416 | "source": { 1417 | "type": "git", 1418 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1419 | "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" 1420 | }, 1421 | "dist": { 1422 | "type": "zip", 1423 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", 1424 | "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", 1425 | "shasum": "" 1426 | }, 1427 | "require": { 1428 | "php": ">=8.2" 1429 | }, 1430 | "require-dev": { 1431 | "phpunit/phpunit": "^11.0" 1432 | }, 1433 | "type": "library", 1434 | "extra": { 1435 | "branch-alias": { 1436 | "dev-main": "4.0-dev" 1437 | } 1438 | }, 1439 | "autoload": { 1440 | "classmap": [ 1441 | "src/" 1442 | ] 1443 | }, 1444 | "notification-url": "https://packagist.org/downloads/", 1445 | "license": [ 1446 | "BSD-3-Clause" 1447 | ], 1448 | "authors": [ 1449 | { 1450 | "name": "Sebastian Bergmann", 1451 | "email": "sebastian@phpunit.de" 1452 | } 1453 | ], 1454 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1455 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1456 | "support": { 1457 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1458 | "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", 1459 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" 1460 | }, 1461 | "funding": [ 1462 | { 1463 | "url": "https://github.com/sebastianbergmann", 1464 | "type": "github" 1465 | } 1466 | ], 1467 | "time": "2024-07-03T05:01:32+00:00" 1468 | }, 1469 | { 1470 | "name": "sebastian/recursion-context", 1471 | "version": "6.0.2", 1472 | "source": { 1473 | "type": "git", 1474 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1475 | "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" 1476 | }, 1477 | "dist": { 1478 | "type": "zip", 1479 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", 1480 | "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", 1481 | "shasum": "" 1482 | }, 1483 | "require": { 1484 | "php": ">=8.2" 1485 | }, 1486 | "require-dev": { 1487 | "phpunit/phpunit": "^11.0" 1488 | }, 1489 | "type": "library", 1490 | "extra": { 1491 | "branch-alias": { 1492 | "dev-main": "6.0-dev" 1493 | } 1494 | }, 1495 | "autoload": { 1496 | "classmap": [ 1497 | "src/" 1498 | ] 1499 | }, 1500 | "notification-url": "https://packagist.org/downloads/", 1501 | "license": [ 1502 | "BSD-3-Clause" 1503 | ], 1504 | "authors": [ 1505 | { 1506 | "name": "Sebastian Bergmann", 1507 | "email": "sebastian@phpunit.de" 1508 | }, 1509 | { 1510 | "name": "Jeff Welch", 1511 | "email": "whatthejeff@gmail.com" 1512 | }, 1513 | { 1514 | "name": "Adam Harvey", 1515 | "email": "aharvey@php.net" 1516 | } 1517 | ], 1518 | "description": "Provides functionality to recursively process PHP variables", 1519 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1520 | "support": { 1521 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1522 | "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", 1523 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" 1524 | }, 1525 | "funding": [ 1526 | { 1527 | "url": "https://github.com/sebastianbergmann", 1528 | "type": "github" 1529 | } 1530 | ], 1531 | "time": "2024-07-03T05:10:34+00:00" 1532 | }, 1533 | { 1534 | "name": "sebastian/type", 1535 | "version": "5.1.0", 1536 | "source": { 1537 | "type": "git", 1538 | "url": "https://github.com/sebastianbergmann/type.git", 1539 | "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" 1540 | }, 1541 | "dist": { 1542 | "type": "zip", 1543 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", 1544 | "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", 1545 | "shasum": "" 1546 | }, 1547 | "require": { 1548 | "php": ">=8.2" 1549 | }, 1550 | "require-dev": { 1551 | "phpunit/phpunit": "^11.3" 1552 | }, 1553 | "type": "library", 1554 | "extra": { 1555 | "branch-alias": { 1556 | "dev-main": "5.1-dev" 1557 | } 1558 | }, 1559 | "autoload": { 1560 | "classmap": [ 1561 | "src/" 1562 | ] 1563 | }, 1564 | "notification-url": "https://packagist.org/downloads/", 1565 | "license": [ 1566 | "BSD-3-Clause" 1567 | ], 1568 | "authors": [ 1569 | { 1570 | "name": "Sebastian Bergmann", 1571 | "email": "sebastian@phpunit.de", 1572 | "role": "lead" 1573 | } 1574 | ], 1575 | "description": "Collection of value objects that represent the types of the PHP type system", 1576 | "homepage": "https://github.com/sebastianbergmann/type", 1577 | "support": { 1578 | "issues": "https://github.com/sebastianbergmann/type/issues", 1579 | "security": "https://github.com/sebastianbergmann/type/security/policy", 1580 | "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" 1581 | }, 1582 | "funding": [ 1583 | { 1584 | "url": "https://github.com/sebastianbergmann", 1585 | "type": "github" 1586 | } 1587 | ], 1588 | "time": "2024-09-17T13:12:04+00:00" 1589 | }, 1590 | { 1591 | "name": "sebastian/version", 1592 | "version": "5.0.2", 1593 | "source": { 1594 | "type": "git", 1595 | "url": "https://github.com/sebastianbergmann/version.git", 1596 | "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" 1597 | }, 1598 | "dist": { 1599 | "type": "zip", 1600 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", 1601 | "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", 1602 | "shasum": "" 1603 | }, 1604 | "require": { 1605 | "php": ">=8.2" 1606 | }, 1607 | "type": "library", 1608 | "extra": { 1609 | "branch-alias": { 1610 | "dev-main": "5.0-dev" 1611 | } 1612 | }, 1613 | "autoload": { 1614 | "classmap": [ 1615 | "src/" 1616 | ] 1617 | }, 1618 | "notification-url": "https://packagist.org/downloads/", 1619 | "license": [ 1620 | "BSD-3-Clause" 1621 | ], 1622 | "authors": [ 1623 | { 1624 | "name": "Sebastian Bergmann", 1625 | "email": "sebastian@phpunit.de", 1626 | "role": "lead" 1627 | } 1628 | ], 1629 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1630 | "homepage": "https://github.com/sebastianbergmann/version", 1631 | "support": { 1632 | "issues": "https://github.com/sebastianbergmann/version/issues", 1633 | "security": "https://github.com/sebastianbergmann/version/security/policy", 1634 | "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" 1635 | }, 1636 | "funding": [ 1637 | { 1638 | "url": "https://github.com/sebastianbergmann", 1639 | "type": "github" 1640 | } 1641 | ], 1642 | "time": "2024-10-09T05:16:32+00:00" 1643 | }, 1644 | { 1645 | "name": "squizlabs/php_codesniffer", 1646 | "version": "3.10.3", 1647 | "source": { 1648 | "type": "git", 1649 | "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", 1650 | "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" 1651 | }, 1652 | "dist": { 1653 | "type": "zip", 1654 | "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", 1655 | "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", 1656 | "shasum": "" 1657 | }, 1658 | "require": { 1659 | "ext-simplexml": "*", 1660 | "ext-tokenizer": "*", 1661 | "ext-xmlwriter": "*", 1662 | "php": ">=5.4.0" 1663 | }, 1664 | "require-dev": { 1665 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" 1666 | }, 1667 | "bin": [ 1668 | "bin/phpcbf", 1669 | "bin/phpcs" 1670 | ], 1671 | "type": "library", 1672 | "extra": { 1673 | "branch-alias": { 1674 | "dev-master": "3.x-dev" 1675 | } 1676 | }, 1677 | "notification-url": "https://packagist.org/downloads/", 1678 | "license": [ 1679 | "BSD-3-Clause" 1680 | ], 1681 | "authors": [ 1682 | { 1683 | "name": "Greg Sherwood", 1684 | "role": "Former lead" 1685 | }, 1686 | { 1687 | "name": "Juliette Reinders Folmer", 1688 | "role": "Current lead" 1689 | }, 1690 | { 1691 | "name": "Contributors", 1692 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" 1693 | } 1694 | ], 1695 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1696 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", 1697 | "keywords": [ 1698 | "phpcs", 1699 | "standards", 1700 | "static analysis" 1701 | ], 1702 | "support": { 1703 | "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", 1704 | "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", 1705 | "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", 1706 | "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" 1707 | }, 1708 | "funding": [ 1709 | { 1710 | "url": "https://github.com/PHPCSStandards", 1711 | "type": "github" 1712 | }, 1713 | { 1714 | "url": "https://github.com/jrfnl", 1715 | "type": "github" 1716 | }, 1717 | { 1718 | "url": "https://opencollective.com/php_codesniffer", 1719 | "type": "open_collective" 1720 | } 1721 | ], 1722 | "time": "2024-09-18T10:38:58+00:00" 1723 | }, 1724 | { 1725 | "name": "theseer/tokenizer", 1726 | "version": "1.2.3", 1727 | "source": { 1728 | "type": "git", 1729 | "url": "https://github.com/theseer/tokenizer.git", 1730 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 1731 | }, 1732 | "dist": { 1733 | "type": "zip", 1734 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1735 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1736 | "shasum": "" 1737 | }, 1738 | "require": { 1739 | "ext-dom": "*", 1740 | "ext-tokenizer": "*", 1741 | "ext-xmlwriter": "*", 1742 | "php": "^7.2 || ^8.0" 1743 | }, 1744 | "type": "library", 1745 | "autoload": { 1746 | "classmap": [ 1747 | "src/" 1748 | ] 1749 | }, 1750 | "notification-url": "https://packagist.org/downloads/", 1751 | "license": [ 1752 | "BSD-3-Clause" 1753 | ], 1754 | "authors": [ 1755 | { 1756 | "name": "Arne Blankerts", 1757 | "email": "arne@blankerts.de", 1758 | "role": "Developer" 1759 | } 1760 | ], 1761 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1762 | "support": { 1763 | "issues": "https://github.com/theseer/tokenizer/issues", 1764 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 1765 | }, 1766 | "funding": [ 1767 | { 1768 | "url": "https://github.com/theseer", 1769 | "type": "github" 1770 | } 1771 | ], 1772 | "time": "2024-03-03T12:36:25+00:00" 1773 | } 1774 | ], 1775 | "aliases": [], 1776 | "minimum-stability": "stable", 1777 | "stability-flags": {}, 1778 | "prefer-stable": false, 1779 | "prefer-lowest": false, 1780 | "platform": { 1781 | "php": "^8.0" 1782 | }, 1783 | "platform-dev": {}, 1784 | "plugin-api-version": "2.6.0" 1785 | } 1786 | -------------------------------------------------------------------------------- /doc/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "https://platesphp.com/" 2 | languageCode = "en-us" 3 | title = "Plates" 4 | 5 | pygmentsUseClasses = true 6 | 7 | googleAnalytics = "UA-46050814-2" 8 | 9 | [params] 10 | tagline = "Native PHP Templates" 11 | description = "Plates is a Twig inspired, native PHP template system that brings modern template language functionality to native PHP templates." 12 | [params.images] 13 | favicon = "favicon/favicon.ico" 14 | appleTouch = "favicon/apple-touch-icon-precomposed.png" 15 | logo = "images/logo.png" -------------------------------------------------------------------------------- /doc/content/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Introduction" 3 | [menu.main] 4 | parent = "getting-started" 5 | weight = 1 6 | +++ 7 | 8 | [![Maintainer](http://img.shields.io/badge/maintainer-@ragboyjr-blue.svg?style=flat-square)](https://twitter.com/reinink) 9 | [![Source Code](http://img.shields.io/badge/source-league/plates-blue.svg?style=flat-square)](https://github.com/thephpleague/plates) 10 | [![Latest Version](https://img.shields.io/github/release/thephpleague/plates.svg?style=flat-square)](https://github.com/thephpleague/plates/releases) 11 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/thephpleague/plates/blob/master/LICENSE) 12 | {{}}
{{}} 13 | [![Build Status](https://img.shields.io/github/workflow/status/thephpleague/plates/PHP/v3?style=flat-square)](https://github.com/thephpleague/plates/actions?query=workflow%3APHP+branch%3Av3) 14 | [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/plates.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/plates) 15 | [![Total Downloads](https://img.shields.io/packagist/dt/league/plates.svg?style=flat-square)](https://packagist.org/packages/league/plates) 16 | 17 | ## About 18 | 19 | Plates is a native PHP template system that's fast, easy to use and easy to extend. It's inspired by the excellent [Twig](http://twig.sensiolabs.org/) template engine and strives to bring modern template language functionality to native PHP templates. Plates is designed for developers who prefer to use native PHP templates over compiled template languages, such as Twig or Smarty. 20 | 21 | ## Highlights 22 | 23 | - Native PHP templates, no new [syntax]({{< relref "templates/syntax.md" >}}) to learn 24 | - Plates is a template system, not a template language 25 | - Plates encourages the use of existing PHP functions 26 | - Increase code reuse with template [layouts]({{< relref "templates/layouts.md" >}}) and [inheritance]({{< relref "templates/inheritance.md" >}}) 27 | - Template [folders]({{< relref "engine/folders.md" >}}) for grouping templates into namespaces 28 | - [Data]({{< relref "templates/data.md#preassigned-and-shared-data" >}}) sharing across templates 29 | - Preassign [data]({{< relref "templates/data#preassigned-and-shared-data" >}}) to specific templates 30 | - Built-in [escaping]({{< relref "templates/escaping.md" >}}) helpers 31 | - Easy to extend using [functions]({{< relref "engine/functions.md" >}}) and [extensions]({{< relref "engine/extensions.md" >}}) 32 | - Framework-agnostic, will work with any project 33 | - Decoupled design makes templates easy to test 34 | - Composer ready and PSR-2 compliant 35 | 36 | ## Questions? 37 | 38 | Plates is maintained by [RJ Garcia](https://twitter.com/ragboyjr) and originally created by [Jonathan Reinink](https://twitter.com/reinink). Submit issues to [Github](https://github.com/thephpleague/plates/issues). 39 | -------------------------------------------------------------------------------- /doc/content/engine/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "The Engine" 3 | [menu.main] 4 | identifier = "engine" 5 | weight = 2 6 | +++ -------------------------------------------------------------------------------- /doc/content/engine/extensions.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Extensions" 3 | linkTitle = "Engine Extensions" 4 | [menu.main] 5 | parent = "engine" 6 | weight = 5 7 | +++ 8 | 9 | Creating extensions couldn't be easier, and can really make Plates sing for your specific project. Start by creating a class that implements `\League\Plates\Extension\ExtensionInterface`. Next, register your template [functions]({{< relref "engine/functions.md" >}}) within a `register()` method. 10 | 11 | ## Simple extensions example 12 | 13 | ~~~ php 14 | use League\Plates\Engine; 15 | use League\Plates\Extension\ExtensionInterface; 16 | 17 | class ChangeCase implements ExtensionInterface 18 | { 19 | public function register(Engine $engine) 20 | { 21 | $engine->registerFunction('uppercase', [$this, 'uppercaseString']); 22 | $engine->registerFunction('lowercase', [$this, 'lowercaseString']); 23 | } 24 | 25 | public function uppercaseString($var) 26 | { 27 | return strtoupper($var); 28 | } 29 | 30 | public function lowercaseString($var) 31 | { 32 | return strtolower($var); 33 | } 34 | } 35 | ~~~ 36 | 37 | To use this extension in your template, simply call your new functions: 38 | 39 | ~~~ php 40 |

Hello, e($this->uppercase($name))?>

41 | ~~~ 42 | 43 | They can also be used in a [batch]({{< relref "templates/functions.md#batch-function-calls" >}}) compatible function: 44 | 45 | ~~~ php 46 |

Hello e($name, 'uppercase')

47 | ~~~ 48 | 49 | ## Single method extensions 50 | 51 | Alternatively, you may choose to expose the entire extension object to the template using a single function. This can make your templates more legible and also reduce the chance of conflicts with other extensions. 52 | 53 | ~~~ php 54 | use League\Plates\Engine; 55 | use League\Plates\Extension\ExtensionInterface; 56 | 57 | class ChangeCase implements ExtensionInterface 58 | { 59 | public function register(Engine $engine) 60 | { 61 | $engine->registerFunction('case', [$this, 'getObject']); 62 | } 63 | 64 | public function getObject() 65 | { 66 | return $this; 67 | } 68 | 69 | public function upper($var) 70 | { 71 | return strtoupper($var); 72 | } 73 | 74 | public function lower($var) 75 | { 76 | return strtolower($var); 77 | } 78 | } 79 | ~~~ 80 | 81 | To use this extension in your template, first call the primary function, then the secondary functions: 82 | 83 | ~~~ php 84 |

Hello, e($this->case()->upper($name))?>

85 | ~~~ 86 | 87 | ## Loading extensions 88 | 89 | To enable an extension, load it into the [engine]({{< relref "engine/overview.md" >}}) object using the `loadExtension()` method. 90 | 91 | ~~~ php 92 | $engine->loadExtension(new ChangeCase()); 93 | ~~~ 94 | 95 | ## Accessing the engine and template 96 | 97 | It may be desirable to access the `engine` or `template` objects from within your extension. Plates makes both of these objects available to you. The engine is automatically passed to the `register()` method, and the template is assigned as a parameter on each function call. 98 | 99 | ~~~ php 100 | use League\Plates\Engine; 101 | use League\Plates\Extension\ExtensionInterface; 102 | 103 | class MyExtension implements ExtensionInterface 104 | { 105 | protected $engine; 106 | public $template; // must be public 107 | 108 | public function register(Engine $engine) 109 | { 110 | $this->engine = $engine; 111 | 112 | // Access template data: 113 | $data = $this->template->data(); 114 | 115 | // Register functions 116 | // ... 117 | } 118 | } 119 | ~~~ 120 | -------------------------------------------------------------------------------- /doc/content/engine/file-extensions.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "File Extensions" 3 | linkTitle = "Engine File Extensions" 4 | [menu.main] 5 | parent = "engine" 6 | weight = 2 7 | +++ 8 | 9 | Plates does not enforce a specific template file extension. By default it assumes `.php`. This file extension is automatically appended to your template names when rendered. You are welcome to change the default extension using one of the following methods. 10 | 11 | ## Constructor method 12 | 13 | ~~~ php 14 | // Create new engine and set the default file extension to ".tpl" 15 | $template = new League\Plates\Engine('/path/to/templates', 'tpl'); 16 | ~~~ 17 | 18 | ## Setter method 19 | 20 | ~~~ php 21 | // Sets the default file extension to ".tpl" after engine instantiation 22 | $template->setFileExtension('tpl'); 23 | ~~~ 24 | 25 | ## Manually assign 26 | 27 | If you prefer to manually set the file extension, simply set the default file extension to `null`. 28 | 29 | ~~~ php 30 | // Disable automatic file extensions 31 | $template->setFileExtension(null); 32 | 33 | // Render template 34 | echo $templates->render('home.php'); 35 | ~~~ -------------------------------------------------------------------------------- /doc/content/engine/folders.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Folders" 3 | linkTitle = "Engine Folders" 4 | [menu.main] 5 | parent = "engine" 6 | weight = 3 7 | +++ 8 | 9 | Folders make it really easy to organize and access your templates. Folders allow you to group your templates under different namespaces, each of which having their own file system path. 10 | 11 | ## Creating folders 12 | 13 | To create folders, use the `addFolder()` method: 14 | 15 | ~~~ php 16 | // Create new Plates instance 17 | $templates = new League\Plates\Engine(); 18 | 19 | // Add folders 20 | $templates->addFolder('admin', '/path/to/admin/templates'); 21 | $templates->addFolder('emails', '/path/to/email/templates'); 22 | ~~~ 23 | 24 | ## Using folders 25 | 26 | To use the folders you created within your project simply append the folder name with two colons before the template name. For example, to render a welcome email: 27 | 28 | ~~~ php 29 | $email = $templates->render('emails::welcome'); 30 | ~~~ 31 | 32 | This works with template functions as well, such as layouts or nested templates. For example: 33 | 34 | ~~~ php 35 | layout('shared::template') ?> 36 | ~~~ 37 | 38 | ## Folder fallbacks 39 | 40 | When enabled, if a folder template is missing, Plates will automatically fallback and look for a template with the **same** name in the default folder. This can be helpful when using folders to manage themes. To enable fallbacks, simply pass `true` as the third parameter in the `addFolders()` method. 41 | 42 | ~~~ php 43 | // Create new Plates engine 44 | $templates = new \League\Plates\Engine('/path/to/default/theme'); 45 | 46 | // Add themes 47 | $templates->addFolder('theme1', '/path/to/theme/1', true); 48 | $templates->addFolder('theme2', '/path/to/theme/2', true); 49 | ~~~ -------------------------------------------------------------------------------- /doc/content/engine/functions.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Functions" 3 | linkTitle = "Engine Functions" 4 | [menu.main] 5 | parent = "engine" 6 | weight = 4 7 | +++ 8 | 9 | While [extensions]({{< relref "engine/extensions.md" >}}) are awesome for adding additional reusable functionality to Plates, sometimes it's easier to just create a one-off function for a specific use case. Plates makes this easy to do. 10 | 11 | ## Registering functions 12 | 13 | ~~~ php 14 | // Create new Plates engine 15 | $templates = new \League\Plates\Engine('/path/to/templates'); 16 | 17 | // Register a one-off function 18 | $templates->registerFunction('uppercase', function ($string) { 19 | return strtoupper($string); 20 | }); 21 | ~~~ 22 | 23 | To use this function in a template, simply call it like any other function: 24 | 25 | ~~~ php 26 |

Hello e($this->uppercase($name))

27 | ~~~ 28 | 29 | It can also be used in a [batch]({{< relref "templates/functions#batch-function-calls">}}) compatible function: 30 | 31 | ~~~ php 32 |

Hello e($name, 'uppercase')

33 | ~~~ 34 | -------------------------------------------------------------------------------- /doc/content/engine/overview.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Overview" 3 | linkTitle = "Engine Overview" 4 | aliases = ["/engine"] 5 | [menu.main] 6 | parent = "engine" 7 | weight = 1 8 | +++ 9 | 10 | Plates uses a central object called the `Engine`, which is used to store the environment configuration, functions and extensions. It helps decouple your templates from the file system and other dependencies. For example, if you want to change the folder where your templates are stored, you can do so by simply changing the path in one location. 11 | 12 | ## Basic usage 13 | 14 | ~~~ php 15 | // Create new Plates engine 16 | $templates = new League\Plates\Engine('/path/to/templates'); 17 | 18 | // Add any additional folders 19 | $templates->addFolder('emails', '/path/to/emails'); 20 | 21 | // Load any additional extensions 22 | $templates->loadExtension(new League\Plates\Extension\Asset('/path/to/public')); 23 | 24 | // Create a new template 25 | $template = $templates->make('emails::welcome'); 26 | ~~~ 27 | 28 | ## Dependency Injection 29 | 30 | Plates is designed to be easily passed around your application and easily injected in your controllers or other application objects. Simply pass an instance of the `Engine` to any consuming objects, and then use either the `make()` method to create a new template, or the `render()` method to render it immediately. For example: 31 | 32 | ~~~ php 33 | class Controller 34 | { 35 | private $templates; 36 | 37 | public function __construct(League\Plates\Engine $templates) 38 | { 39 | $this->templates = $templates; 40 | } 41 | 42 | // Create a template object 43 | public function getIndex() 44 | { 45 | $template = $this->templates->make('home'); 46 | 47 | return $template->render(); 48 | } 49 | 50 | // Render a template directly 51 | public function getIndex() 52 | { 53 | return $this->templates->render('home'); 54 | } 55 | } 56 | ~~~ 57 | -------------------------------------------------------------------------------- /doc/content/engine/themes.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Themes" 3 | linkTitle = "Engine Themes" 4 | [menu.main] 5 | parent = "engine" 6 | weight = 6 7 | [menu.main.params] 8 | badge = "v3.5" 9 | +++ 10 | 11 | Themes provide an alternative to template path resolution that allow for a holistic approach to template overrides and fallbacks. 12 | 13 | ## Usage 14 | 15 | Given an engine configuration like: 16 | 17 | ```php 18 | use League\Plates\{Engine, Template\Theme}; 19 | 20 | $plates = Engine::fromTheme(Theme::hierarchy([ 21 | Theme::new('/templates/main', 'Main'), // parent 22 | Theme::new('/templates/user', 'User'), // child 23 | Theme::new('/templates/seasonal', 'Seasonal'), // child2 24 | ])); 25 | ``` 26 | 27 | And a file structure like: 28 | 29 | ``` 30 | templates/ 31 | main/ 32 | layout.php 33 | home.php 34 | header.php 35 | user/ 36 | layout.php 37 | header.php 38 | seasonal/ 39 | header.php 40 | ``` 41 | 42 | The following looks ups, *regardless of where they are called from*, would resolve to the following files: 43 | 44 | ```php 45 | $templates->render('home'); // templates/main/home.php 46 | $templates->render('layout'); // templates/user/layout.php 47 | $templates->render('header'); // templates/seasonal/header.php 48 | ``` 49 | 50 | All paths are resolved from the last child to the first parent allowing a hierarchy of overrides. 51 | 52 | ## Differences from Directory and Folders 53 | 54 | This logic is used **instead of** the directories and folders feature since they are distinct in nature, and combining the logic isn't obvious on how the features should stack. 55 | 56 | Creating an engine with one theme is functionally equivalent to using just a directory with no folders. 57 | 58 | The fallback functionality is a bit different however since with folders, it's *opt in*, you need to prefix the template name with the folder name. With themes, all template names implicitly will be resolved and fallback according to the hierarchy setup. 59 | 60 | ## Additional Customization 61 | 62 | This functionality is powered by the `League\Plates\Template\ResolveTemplatePath` interface. If you'd prefer a more complex or specific path resolution, you can just implement your own and assign it to the engine instance with: 63 | 64 | ```php 65 | $plates = Engine::withResolveTemplatePath(new MyCustomResolveTemplatePath()); 66 | ``` 67 | 68 | The resolve template path should always resolve a string that represents a verified path on the filesystem or throw a TemplateNotFound exception. 69 | -------------------------------------------------------------------------------- /doc/content/extensions/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Extensions" 3 | [menu.main] 4 | identifier = "extensions" 5 | weight = 4 6 | +++ -------------------------------------------------------------------------------- /doc/content/extensions/asset.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Asset" 3 | [menu.main] 4 | parent = "extensions" 5 | weight = 1 6 | +++ 7 | 8 | The asset extension can be used to quickly create "cache busted" asset URLs in your templates. This is particularly helpful for aggressively cached files that can potentially change in the future, such as CSS files, JavaScript files and images. It works by appending the timestamp of the file's last update to its URL. For example, `/css/all.css` becomes `/css/all.1373577602.css`. As long as the file does not change, the timestamp remains the same and caching occurs. However, if the file is changed, a new URL is automatically generated with a new timestamp, and visitors receive the new file. 9 | 10 | ## Installing the asset extension 11 | 12 | The asset extension comes packaged with Plates but is not enabled by default, as it requires extra parameters passed to it at instantiation. 13 | 14 | ~~~ php 15 | // Load asset extension 16 | $engine->loadExtension(new League\Plates\Extension\Asset('/path/to/public/assets/', true)); 17 | ~~~ 18 | 19 | The first constructor parameter is the file system path of the assets directory. The second is an optional `boolean` parameter that if set to true uses the filename caching method (ie. `file.1373577602.css`) instead of the default query string method (ie. `file.css?v=1373577602`). 20 | 21 | ## Filename caching 22 | 23 | To make filename caching work, some URL rewriting is required: 24 | 25 | ### Apache example 26 | ~~~ php 27 | 28 | RewriteCond %{REQUEST_FILENAME} !-f 29 | RewriteCond %{REQUEST_FILENAME} !-d 30 | RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 31 | 32 | ~~~ 33 | 34 | ### Nginx example 35 | 36 | ~~~ php 37 | location ~* (.+)\.(?:\d+)\.(js|css|png|jpg|jpeg|gif)$ { 38 | try_files $uri $1.$2; 39 | } 40 | ~~~ 41 | 42 | ## Using the asset extension 43 | 44 | ~~~ php 45 | 46 | 47 | Asset Extension Example 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ~~~ 58 | -------------------------------------------------------------------------------- /doc/content/extensions/community.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Community" 3 | [menu.main] 4 | parent = "extensions" 5 | weight = 3 6 | +++ 7 | 8 | This is a list of all the known community extensions for the Plates library. Please feel free to submit a [Pull Request](https://github.com/thephpleague/plates) to add your extension to this list. 9 | 10 | - [Laravel Provider](https://github.com/franzliedke/laravel-plates) 11 | - [Attributes Rendering](https://github.com/RobinDev/platesAttributes) - Transforms arrays into html tag attributes. 12 | - [Includer](https://github.com/odahcam/plates-includer) - Include your assets in an expert way. 13 | - [Tapestry](https://github.com/tapestry-cloud/tapestry) - A blog aware, Plates based static site generator. 14 | - [Plates Local Assets Copy Extension](https://github.com/basteyy/plates-local-assets-copy) - An extension for copying remote assets (files) to your local storage. 15 | -------------------------------------------------------------------------------- /doc/content/extensions/uri.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "URI" 3 | [menu.main] 4 | parent = "extensions" 5 | weight = 2 6 | +++ 7 | 8 | The URI extension is designed to make URI checks within templates easier. The most common use is marking the current page in a menu as "selected". It only has one function, `uri()`, but can do a number of helpful tasks depending on the parameters passed to it. 9 | 10 | ## Installing the URI extension 11 | 12 | The URI extension comes packaged with Plates but is not enabled by default, as it requires an extra parameter passed to it at instantiation. 13 | 14 | ~~~ php 15 | // Load URI extension using global variable 16 | $engine->loadExtension(new League\Plates\Extension\URI($_SERVER['PATH_INFO'])); 17 | 18 | // Load URI extension using a HttpFoundation's request object 19 | $engine->loadExtension(new League\Plates\Extension\URI($request->getPathInfo())); 20 | ~~~ 21 | 22 | ## URI example 23 | 24 | ~~~ php 25 | 31 | ~~~ 32 | 33 | ## Using the URI extension 34 | 35 | Get the whole URI. 36 | 37 | ~~~ php 38 | uri()?> 39 | ~~~ 40 | 41 | Get a specified segment of the URI. 42 | 43 | ~~~ php 44 | uri(1)?> 45 | ~~~ 46 | 47 | Check if a specific segment of the URI (first parameter) equals a given string (second parameter). Returns `true` on success or `false` on failure. 48 | 49 | ~~~ php 50 | uri(1, 'home')): ?> 51 | ~~~ 52 | 53 | Check if a specific segment of the URI (first parameter) equals a given string (second parameter). Returns string (third parameter) on success or `false` on failure. 54 | 55 | ~~~ php 56 | uri(1, 'home', 'success')?> 57 | ~~~ 58 | 59 | Check if a specific segment of the URI (first parameter) equals a given string (second parameter). Returns string (third parameter) on success or string (fourth parameter) on failure. 60 | 61 | ~~~ php 62 | uri(1, 'home', 'success', 'fail')?> 63 | ~~~ 64 | 65 | Check if a regular expression string matches the current URI. Returns `true` on success or `false` on failure. 66 | 67 | ~~~ php 68 | uri('/home')): ?> 69 | ~~~ 70 | 71 | Check if a regular expression string (first parameter) matches the current URI. Returns string (second parameter) on success or `false` on failure. 72 | 73 | ~~~ php 74 | uri('/home', 'success')?> 75 | ~~~ 76 | 77 | Check if a regular expression string (first parameter) matches the current URI. Returns string (second parameter) on success or string (third parameter) on failure. 78 | 79 | ~~~ php 80 | uri('/home', 'success', 'fail')?> 81 | ~~~ -------------------------------------------------------------------------------- /doc/content/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Getting Started" 3 | [menu.main] 4 | identifier = "getting-started" 5 | weight = 1 6 | +++ -------------------------------------------------------------------------------- /doc/content/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Installation" 3 | [menu.main] 4 | parent = "getting-started" 5 | weight = 3 6 | +++ 7 | 8 | ## Using Composer 9 | 10 | Plates is available on [Packagist](https://packagist.org/packages/league/plates) and can be installed using [Composer](https://getcomposer.org/). This can be done by running the following command or by updating your `composer.json` file. 11 | 12 | ~~~ bash 13 | composer require league/plates 14 | ~~~ 15 | 16 | {{< code-filename composer.json >}} 17 | ~~~ javascript 18 | { 19 | "require": { 20 | "league/plates": "3.*" 21 | } 22 | } 23 | ~~~ 24 | 25 | Be sure to also include your Composer autoload file in your project: 26 | 27 | ~~~ php 28 | require 'vendor/autoload.php'; 29 | ~~~ 30 | 31 | ## Downloading .zip file 32 | 33 | This project is also available for download as a `.zip` file on GitHub. Visit the [releases page](https://github.com/thephpleague/plates/releases), select the version you want, and click the "Source code (zip)" download button. -------------------------------------------------------------------------------- /doc/content/getting-started/simple-example.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Simple Example" 3 | [menu.main] 4 | parent = "getting-started" 5 | weight = 2 6 | +++ 7 | 8 | Here is a simple example of how to use Plates. We will assume the following directory stucture: 9 | 10 | ~~~ 11 | `-- path 12 | `-- to 13 | `-- templates 14 | |-- template.php 15 | |-- profile.php 16 | ~~~ 17 | 18 | ## Within your controller 19 | 20 | ~~~ php 21 | // Create new Plates instance 22 | $templates = new League\Plates\Engine('/path/to/templates'); 23 | 24 | // Render a template 25 | echo $templates->render('profile', ['name' => 'Jonathan']); 26 | ~~~ 27 | 28 | ## The page template 29 | 30 | {{< code-filename profile.php >}} 31 | ~~~ php 32 | layout('template', ['title' => 'User Profile']) ?> 33 | 34 |

User Profile

35 |

Hello, e($name)?>

36 | ~~~ 37 | 38 | ## The layout template 39 | 40 | {{< code-filename template.php >}} 41 | ~~~ php 42 | 43 | 44 | <?=$this->e($title)?> 45 | 46 | 47 | section('content')?> 48 | 49 | 50 | ~~~ -------------------------------------------------------------------------------- /doc/content/templates/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Templates" 3 | [menu.main] 4 | identifier = "templates" 5 | weight = 3 6 | +++ -------------------------------------------------------------------------------- /doc/content/templates/data.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Data" 3 | linkTitle = "Templates Data" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 2 7 | +++ 8 | 9 | It's very common to share application data (variables) with a template. Data can be whatever you want: strings, arrays, objects, etc. Plates allows you set both template specific data as well as shared template data. 10 | 11 | ## Assign data 12 | 13 | Assigning data is done from within your application code, such as a controller. There are a number of ways to assign the data, depending on how you structure your objects. 14 | 15 | ~~~ php 16 | // Create new Plates instance 17 | $templates = new League\Plates\Engine('/path/to/templates'); 18 | 19 | // Assign via the engine's render method 20 | echo $templates->render('profile', ['name' => 'Jonathan']); 21 | 22 | // Assign via the engine's make method 23 | $template = $templates->make('profile', ['name' => 'Jonathan']); 24 | 25 | // Assign directly to a template object 26 | $template = $templates->make('profile'); 27 | $template->data(['name' => 'Jonathan']); 28 | ~~~ 29 | 30 | ## Accessing data 31 | 32 | Template data is available as locally scoped variables at the time of rendering. Continuing with the example above, here is how you would [escape]({{< relref "templates/escaping.md" >}}) and output the "name" value in a template: 33 | 34 | ~~~ php 35 |

Hello e($name)?>

36 | ~~~ 37 | 38 |

Prior to Plates 3.0, variables were accessed using the $this pseudo-variable. This is no longer possible. Use the locally scoped variables instead.

39 | 40 | ## Preassigned and shared data 41 | 42 | If you have data that you want assigned to a specific template each time that template is rendered throughout your application, the `addData()` function can help organize that code in one place. 43 | 44 | ~~~ php 45 | $templates->addData(['name' => 'Jonathan'], 'emails::welcome'); 46 | ~~~ 47 | 48 | You can pressaign data to more than one template by passing an array of templates: 49 | 50 | ~~~ php 51 | $templates->addData(['name' => 'Jonathan'], ['login', 'template']); 52 | ~~~ 53 | 54 | To assign data to ALL templates, simply omit the second parameter: 55 | 56 | ~~~ php 57 | $templates->addData(['name' => 'Jonathan']); 58 | ~~~ 59 | 60 | Keep in mind that shared data is assigned to a template when it's first created, meaning any conflicting data assigned that's afterwards to a specific template will overwrite the shared data. This is generally desired behavior. 61 | -------------------------------------------------------------------------------- /doc/content/templates/escaping.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Escaping" 3 | linkTitle = "Templates Escaping" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 8 7 | +++ 8 | 9 | Escaping is a form of [data filtering](http://www.phptherightway.com/#data_filtering) which sanitizes unsafe, user supplied input prior to outputting it as HTML. Plates provides two shortcuts to the `htmlspecialchars()` function. 10 | 11 | ## Escaping example 12 | 13 | ~~~ php 14 |

Hello, escape($name)?>

15 | 16 | 17 |

Hello, e($name)?>

18 | ~~~ 19 | 20 | ## Batch function calls 21 | 22 | The escape functions also support [batch]({{< relref "templates/functions.md#batch-function-calls" >}}) function calls, which allow you to apply multiple functions, including native PHP functions, to a variable at one time. 23 | 24 | ~~~ php 25 |

Welcome e($name, 'strip_tags|strtoupper')?>

26 | ~~~ 27 | 28 | ## Escaping HTML attributes 29 | 30 |

It's VERY important to always double quote HTML attributes that contain escaped variables, otherwise your template will still be open to injection attacks.

31 | 32 | Some [libraries](http://framework.zend.com/manual/2.1/en/modules/zend.escaper.escaping-html-attributes.html) go as far as having a special function for escaping HTML attributes. However, this is somewhat redundant considering that if a developer forgets to properly quote an HTML attribute, they will likely also forget to use this special function. Here is how you properly escape HTML attributes: 33 | 34 | ~~~ php 35 | 36 | <?=$this->e($name)?> 37 | 38 | 39 | <?=$this->e($name)?> 40 | 41 | 42 | <?=$this-e($name)?>> 43 | ~~~ 44 | 45 | ## Automatic escaping 46 | 47 | Probably the biggest drawbacks to native PHP templates is the inability to auto-escape variables properly. Template languages like Twig and Smarty can identify "echoed" variables during a parsing stage and automatically escape them. This cannot be done in native PHP as the language does not offer overloading functionality for it's output functions (ie. `print` and `echo`). 48 | 49 | Don't worry, escaping can still be done safely, it just means you are responsible for manually escaping each variable on output. Consider creating a snippet for one of the above, built-in escaping functions to make this process easier. 50 | -------------------------------------------------------------------------------- /doc/content/templates/functions.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Functions" 3 | linkTitle = "Templates Functions" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 3 7 | +++ 8 | 9 | Template functions in Plates are accessed using the `$this` pseudo-variable. 10 | 11 | ~~~ php 12 |

Hello, escape($name)?>

13 | ~~~ 14 | 15 | 16 | ## Custom fuctions 17 | 18 | In addition to the functions included with Plates, it's also possible to add [one-off functions]({{< relref "engine/functions.md" >}}), or even groups of functions, known as [extensions]({{< relref "engine/extensions.md" >}}). 19 | 20 | ## Batch function calls 21 | 22 | Sometimes you need to apply more than function to a variable in your templates. This can become somewhat illegible. The `batch()` function helps by allowing you to apply multiple functions, including native PHP functions, to a variable at one time. 23 | 24 | ~~~ php 25 | 26 |

Welcome escape(strtoupper(strip_tags($name)))?>

27 | 28 | 29 |

Welcome batch($name, 'strip_tags|strtoupper|escape')?>

30 | ~~~ 31 | 32 | The [escape]({{< relref "templates/escaping.md" >}}) functions also support batch function calls. 33 | 34 | ~~~ php 35 |

Welcome e($name, 'strip_tags|strtoupper')?>

36 | ~~~ 37 | 38 | The batch functions works well for "piped" functions that accept one parameter, modify it, and then return it. It's important to note that they execute functions left to right and will favour extension functions over native PHP functions if there are conflicts. 39 | 40 | ~~~ php 41 | 42 | batch('Jonathan', 'escape|strtolower|strtoupper')?> 43 | 44 | 45 | batch('Jonathan', 'escape|strtoupper|strtolower')?> 46 | ~~~ 47 | -------------------------------------------------------------------------------- /doc/content/templates/inheritance.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Inheritance" 3 | linkTitle = "Templates Inheritance" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 7 7 | +++ 8 | 9 | By combining [layouts]({{< relref "templates/layouts.md" >}}) and [sections]({{< relref "templates/sections.md" >}}), Plates allows you to "build up" your pages using predefined sections. This is best understand using an example: 10 | 11 | ## Inheritance example 12 | 13 | The following example illustrates a pretty standard website. Start by creating a site template, which includes your header and footer as well as any predefined content [sections]({{< relref "templates/sections.md" >}}). Notice how Plates makes it possible to even set default section content, in the event that a page doesn't define it. 14 | 15 | {{< code-filename template.php >}} 16 | 17 | ~~~ php 18 | 19 | 20 | <?=$this->e($title)?> 21 | 22 | 23 | 24 | 25 | 26 |
27 | section('page')?> 28 |
29 | 30 | 37 | 38 | 39 | 40 | ~~~ 41 | 42 | With the template defined, any page can now "implement" this [layout]({{< relref "templates/layouts.md" >}}). Notice how each section of content is defined between the `start()` and `end()` functions. 43 | 44 | {{< code-filename profile.php >}} 45 | 46 | ~~~ php 47 | layout('template', ['title' => 'User Profile']) ?> 48 | 49 | start('page') ?> 50 |

Welcome!

51 |

Hello e($name)?>

52 | stop() ?> 53 | 54 | start('sidebar') ?> 55 | 62 | stop() ?> 63 | ~~~ 64 | -------------------------------------------------------------------------------- /doc/content/templates/layouts.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Layouts" 3 | linkTitle = "Templates Layouts" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 5 7 | +++ 8 | 9 | The `layout()` function allows you to define a layout template that a template will implement. It's like having separate header and footer templates in one file. 10 | 11 | ## Define a layout 12 | 13 | The `layout()` function can be called anywhere in a template, since the layout template is actually rendered second. Typically it's placed at the top of the file. 14 | 15 | ~~~ php 16 | layout('template') ?> 17 | 18 |

User Profile

19 |

Hello, e($name)?>

20 | ~~~ 21 | 22 | This function also works with [folders]({{< relref "engine/folders.md" >}}): 23 | 24 | ~~~ php 25 | layout('shared::template') ?> 26 | ~~~ 27 | 28 | ## Assign data 29 | 30 | To assign data (variables) to a layout template, pass them as an array to the `layout()` function. This data will then be available as locally scoped variables within the layout template. 31 | 32 | ~~~ php 33 | layout('template', ['title' => 'User Profile']) ?> 34 | ~~~ 35 | 36 | ## Accessing the content 37 | 38 | To access the rendered template content within the layout, use the `section()` function, passing `'content'` as the section name. This will return all outputted content from the template that hasn't been defined in a [section]({{< relref "templates/sections.md" >}}). 39 | 40 | ~~~ php 41 | 42 | 43 | <?=$this->e($title)?> 44 | 45 | 46 | 47 | section('content')?> 48 | 49 | 50 | 51 | ~~~ 52 | 53 | ## Stacked layouts 54 | 55 | Plates allows stacking of layouts, allowing even further simplification and organization of templates. Instead of just using one main layout, it's possible to break templates into more specific layouts, which themselves implement a main layout. Consider this example: 56 | 57 | ### The main site layout 58 | 59 | {{< code-filename template.php >}} 60 | 61 | ~~~ php 62 | 63 | 64 | <?=$this->e($title)?> 65 | 66 | 67 | 68 | section('content')?> 69 | 70 | 71 | 72 | ~~~ 73 | 74 | ### The blog layout 75 | 76 | {{< code-filename blog.php >}} 77 | 78 | ~~~ php 79 | layout('template', ['title' => $title]) ?> 80 | 81 |

The Blog

82 | 83 |
84 |
85 | section('content')?> 86 |
87 | 90 |
91 | ~~~ 92 | 93 | ### A blog article 94 | 95 | {{< code-filename blog-article.php >}} 96 | 97 | ~~~ php 98 | layout('blog', ['title' => $article->title]) ?> 99 | 100 |

e($article->title)?>

101 |
102 | e($article->content)?> 103 |
104 | ~~~ 105 | -------------------------------------------------------------------------------- /doc/content/templates/nesting.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Nesting" 3 | linkTitle = "Templates Nesting" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 4 7 | +++ 8 | 9 | Including another template into the current template is done using the `insert()` function: 10 | 11 | ~~~ php 12 | insert('partials/header') ?> 13 | 14 |

Your content.

15 | 16 | insert('partials/footer') ?> 17 | ~~~ 18 | 19 | The `insert()` function also works with [folders]({{< relref "engine/folders.md" >}}): 20 | 21 | ~~~ php 22 | insert('partials::header') ?> 23 | ~~~ 24 | 25 | ## Alternative syntax 26 | 27 | The `insert()` function automatically outputs the rendered template. If you prefer to manually output the response, use the `fetch()` function instead: 28 | 29 | ~~~ php 30 | fetch('partials/header')?> 31 | ~~~ 32 | 33 | ## Assign data 34 | 35 | To assign data (variables) to a nested template, pass them as an array to the `insert()` or `fetch()` functions. This data will then be available as locally scoped variables within the nested template. 36 | 37 | ~~~ php 38 | insert('partials/header', ['name' => 'Jonathan']) ?> 39 | 40 |

Your content.

41 | 42 | insert('partials/footer') ?> 43 | ~~~ 44 | -------------------------------------------------------------------------------- /doc/content/templates/overview.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Overview" 3 | linkTitle = "Templates Overview" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 1 7 | +++ 8 | 9 | Plates templates are very simple PHP objects. Generally you'll want to create these using the two factory methods, `make()` and `render()`, in the [engine]({{< relref "engine/overview.md" >}}). For example: 10 | 11 | ~~~ php 12 | // Create new Plates instance 13 | $templates = new League\Plates\Engine('/path/to/templates'); 14 | 15 | // Render a template in a subdirectory 16 | echo $templates->render('partials/header'); 17 | 18 | // Render a template 19 | echo $templates->render('profile', ['name' => 'Jonathan']); 20 | ~~~ 21 | 22 | For more information about how Plates is designed to be easily added to your application, see the section on [dependency injection]({{< relref "engine/overview.md#dependency-injection" >}}). 23 | 24 | ## Manually creating templates 25 | 26 | It's also possible to create templates manually. The only dependency they require is an instance of the [engine]({{< relref "engine/overview.md" >}}) object. For example: 27 | 28 | ~~~ php 29 | // Create new Plates instance 30 | $templates = new League\Plates\Engine('/path/to/templates'); 31 | 32 | // Create a new template 33 | $template = new League\Plates\Template\Template($templates, 'profile'); 34 | 35 | // Render the template 36 | echo $template->render(['name' => 'Jonathan']); 37 | 38 | // You can also render the template using the toString() magic method 39 | echo $template; 40 | ~~~ 41 | 42 | ## Check if a template exists 43 | 44 | When dynamically loading templates, you may need to check if they exist. This can be done using the engine's `exists()` method: 45 | 46 | ~~~ php 47 | if ($templates->exists('articles::beginners_guide')) { 48 | // It exists! 49 | } 50 | ~~~ 51 | 52 | You can also run this check on an existing template: 53 | 54 | ~~~ php 55 | if ($template->exists()) { 56 | // It exists! 57 | } 58 | ~~~ 59 | 60 | ## Get a template path 61 | 62 | To get a template path from its name, use the engine's `path()` method: 63 | 64 | ~~~ php 65 | $path = $templates->path('articles::beginners_guide'); 66 | ~~~ 67 | 68 | You can also get the path from an existing template: 69 | 70 | ~~~ php 71 | $path = $template->path(); 72 | ~~~ 73 | -------------------------------------------------------------------------------- /doc/content/templates/sections.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Sections" 3 | linkTitle = "Templates Sections" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 6 7 | +++ 8 | 9 | The `start()` and `stop` functions allow you to build sections (or blocks) of content within your template, and instead of them being rendered directly, they are saved for use elsewhere. For example, in your [layout]({{< relref "templates/layouts.md" >}}) template. 10 | 11 | ## Creating sections 12 | 13 | You define the name of the section with the `start()` function. To end a section call the `stop()` function. 14 | 15 | ~~~ php 16 | start('welcome') ?> 17 | 18 |

Welcome!

19 |

Hello e($name)?>

20 | 21 | stop() ?> 22 | ~~~ 23 | 24 | ## Stacking section content 25 | 26 | By default, when you render a section its content will overwrite any existing content for that section. However, it's possible to append/prepend (or stack) the content instead using the `push()` or `unshift()` method respectively. This can be useful for specifying any JavaScript libraries or CSS files required by your child views. 27 | 28 | ~~~ php 29 | push('scripts') ?> 30 | 31 | end() ?> 32 | 33 | unshift('styles') ?> 34 | 35 | end() ?> 36 | ~~~ 37 | 38 |

The end() function is simply an alias of stop(). These functions can be used interchangeably.

39 | 40 | ## Accessing section content 41 | 42 | Access rendered section content using the name you assigned in the `start()` method. This variable can be accessed from the current template and layout templates using the `section()` function. 43 | 44 | ~~~ php 45 | section('welcome')?> 46 | ~~~ 47 | 48 |

Prior to Plates 3.0, accessing template content was done using either the content() or child() functions. For consistency with sections, this is no longer possible.

49 | 50 | ## Default section content 51 | 52 | In situations where a page doesn't implement a particular section, it's helpful to assign default content. There are a couple ways to do this: 53 | 54 | ### Defining it inline 55 | 56 | If the default content can be defined in a single line of code, it's best to simply pass it as the second parameter of the `section()` function. 57 | 58 | ~~~ php 59 | 62 | ~~~ 63 | 64 | ### Use an if statement 65 | 66 | If the default content requires more than a single line of code, it's best to use a simple if statement to check if a section exists, and otherwise display the default. 67 | 68 | ~~~ php 69 | 82 | ~~~ 83 | 84 | -------------------------------------------------------------------------------- /doc/content/templates/syntax.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Syntax" 3 | linkTitle = "Templates Syntax" 4 | [menu.main] 5 | parent = "templates" 6 | weight = 9 7 | +++ 8 | 9 | While the actual syntax you use in your templates is entirely your choice (it's just PHP after all), we suggest the following syntax guidelines to help keep templates clean and legible. 10 | 11 | ## Guidelines 12 | 13 | - Always use HTML with inline PHP. Never use blocks of PHP. 14 | - Always escape potentially dangerous variables prior to outputting using the built-in escape functions. More on escaping [here]({{< relref "templates/escaping.md" >}}). 15 | - Always use the short echo syntax (`layout('template', ['title' => 'User Profile']) ?> 30 | 31 |

Welcome!

32 |

Hello e($name)?>

33 | 34 |

Friends

35 | 44 | 45 | 46 |

Invitations

47 |

You have some friend invites!

48 | 49 | ~~~ 50 | -------------------------------------------------------------------------------- /doc/layouts/_default/baseof.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ if .IsHome }} 8 | {{ .Site.Title }} - {{ .Site.Params.tagline }} 9 | {{ else }} 10 | {{ partial "title" . }} | {{ .Site.Title -}} 11 | {{ end }} 12 | {{ if .Site.Params.description }} 13 | 14 | {{ end }} 15 | {{ if .Site.Params.Images.favicon }} 16 | 17 | {{ else }} 18 | 19 | {{ end }} 20 | {{ if .Site.Params.Images.appleTouch }} 21 | 22 | {{ else }} 23 | 24 | {{ end }} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Fork me on GitHub 33 | 34 | 35 |
36 | 37 | The League of Extraordinary Packages 38 | 39 |

Our Packages:

40 | 43 |
44 | 45 |
46 | 55 | 56 | Presented by The League of Extraordinary Packages 57 | 58 |
59 | 60 | 61 | 65 | 66 |
67 | 68 | {{ $currentPage := . }} 69 | {{ range .Site.Menus.main }} 70 |

{{ .Name }}

71 | 83 | {{ end }} 84 |
85 |
86 | {{ template "main" . }} 87 |
88 |
89 | 90 | 94 | 95 | 96 | 97 | 98 | 99 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /doc/layouts/_default/list.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |

{{ .Title }}

3 | {{ .Content }} 4 | {{ end }} -------------------------------------------------------------------------------- /doc/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |

{{ .Title }}

3 | {{ .Content }} 4 | {{ end }} -------------------------------------------------------------------------------- /doc/layouts/partials/title.html: -------------------------------------------------------------------------------- 1 | {{ $title := "" }} 2 | 3 | {{ if .Title }} 4 | {{ $title = .Title }} 5 | {{ else if and .IsSection .File }} 6 | {{ $title = path.Base .File.Dir | humanize | title }} 7 | {{ else if and .IsPage .File }} 8 | {{ $title = .File.BaseFileName | humanize | title }} 9 | {{ end }} 10 | 11 | {{ return $title }} -------------------------------------------------------------------------------- /doc/layouts/shortcodes/code-filename.html: -------------------------------------------------------------------------------- 1 |
{{ index .Params 0 }}
-------------------------------------------------------------------------------- /doc/layouts/shortcodes/html.html: -------------------------------------------------------------------------------- 1 | {{/* ref: https://anaulin.org/blog/hugo-raw-html-shortcode/ */}} 2 | 3 | {{.Inner}} -------------------------------------------------------------------------------- /doc/static/CNAME: -------------------------------------------------------------------------------- 1 | platesphp.com -------------------------------------------------------------------------------- /doc/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .github { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | border: 0; 6 | z-index: 1000; 7 | } 8 | 9 | @media screen and (max-width: 1065px) { 10 | .github { 11 | display: none; 12 | } 13 | } 14 | 15 | *:focus { 16 | outline: none; 17 | } 18 | .select2-container { 19 | font-family: "Museo 300"; 20 | } 21 | .version-select { 22 | margin: 8px 25px 0px 45px; 23 | } 24 | 25 | .menu-badge { 26 | color: #ff4143; 27 | } -------------------------------------------------------------------------------- /doc/static/favicon/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thephpleague/plates/5fd1ed82ca25d600a800877f36bb344d1db0c1c7/doc/static/favicon/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /doc/static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thephpleague/plates/5fd1ed82ca25d600a800877f36bb344d1db0c1c7/doc/static/favicon/favicon.ico -------------------------------------------------------------------------------- /doc/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thephpleague/plates/5fd1ed82ca25d600a800877f36bb344d1db0c1c7/doc/static/images/logo.png -------------------------------------------------------------------------------- /example/example.php: -------------------------------------------------------------------------------- 1 | addData(['company' => 'The Company Name'], 'layout'); 10 | 11 | // Render a template 12 | echo $templates->render('profile', ['name' => 'Jonathan']); 13 | -------------------------------------------------------------------------------- /example/templates/layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | <?=$this->e($title)?> | <?=$this->e($company)?> 4 | 5 | 6 | 7 | section('content')?> 8 | 9 | section('scripts')?> 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/templates/profile.php: -------------------------------------------------------------------------------- 1 | layout('layout', ['title' => 'User Profile']) ?> 2 | 3 |

User Profile

4 |

Hello, e($name)?>!

5 | 6 | insert('sidebar') ?> 7 | 8 | push('scripts') ?> 9 | 12 | end() ?> -------------------------------------------------------------------------------- /example/templates/sidebar.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | src 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Engine.php: -------------------------------------------------------------------------------- 1 | directory = new Directory($directory); 63 | $this->fileExtension = new FileExtension($fileExtension); 64 | $this->folders = new Folders(); 65 | $this->functions = new Functions(); 66 | $this->data = new Data(); 67 | $this->resolveTemplatePath = new ResolveTemplatePath\NameAndFolderResolveTemplatePath(); 68 | } 69 | 70 | public static function fromTheme(Theme $theme, string $fileExtension = 'php'): self { 71 | $engine = new self(null, $fileExtension); 72 | $engine->setResolveTemplatePath(new ResolveTemplatePath\ThemeResolveTemplatePath($theme)); 73 | return $engine; 74 | } 75 | 76 | public function setResolveTemplatePath(ResolveTemplatePath $resolveTemplatePath) 77 | { 78 | $this->resolveTemplatePath = $resolveTemplatePath; 79 | 80 | return $this; 81 | } 82 | 83 | public function getResolveTemplatePath(): ResolveTemplatePath 84 | { 85 | return $this->resolveTemplatePath; 86 | } 87 | 88 | /** 89 | * Set path to templates directory. 90 | * @param string|null $directory Pass null to disable the default directory. 91 | * @return Engine 92 | */ 93 | public function setDirectory($directory) 94 | { 95 | $this->directory->set($directory); 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Get path to templates directory. 102 | * @return string 103 | */ 104 | public function getDirectory() 105 | { 106 | return $this->directory->get(); 107 | } 108 | 109 | /** 110 | * Set the template file extension. 111 | * @param string|null $fileExtension Pass null to manually set it. 112 | * @return Engine 113 | */ 114 | public function setFileExtension($fileExtension) 115 | { 116 | $this->fileExtension->set($fileExtension); 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * Get the template file extension. 123 | * @return string 124 | */ 125 | public function getFileExtension() 126 | { 127 | return $this->fileExtension->get(); 128 | } 129 | 130 | /** 131 | * Add a new template folder for grouping templates under different namespaces. 132 | * @param string $name 133 | * @param string $directory 134 | * @param boolean $fallback 135 | * @return Engine 136 | */ 137 | public function addFolder($name, $directory, $fallback = false) 138 | { 139 | $this->folders->add($name, $directory, $fallback); 140 | 141 | return $this; 142 | } 143 | 144 | /** 145 | * Remove a template folder. 146 | * @param string $name 147 | * @return Engine 148 | */ 149 | public function removeFolder($name) 150 | { 151 | $this->folders->remove($name); 152 | 153 | return $this; 154 | } 155 | 156 | /** 157 | * Get collection of all template folders. 158 | * @return Folders 159 | */ 160 | public function getFolders() 161 | { 162 | return $this->folders; 163 | } 164 | 165 | /** 166 | * Add preassigned template data. 167 | * @param array $data; 168 | * @param null|string|array $templates; 169 | * @return Engine 170 | */ 171 | public function addData(array $data, $templates = null) 172 | { 173 | $this->data->add($data, $templates); 174 | 175 | return $this; 176 | } 177 | 178 | /** 179 | * Get all preassigned template data. 180 | * @param null|string $template; 181 | * @return array 182 | */ 183 | public function getData($template = null) 184 | { 185 | return $this->data->get($template); 186 | } 187 | 188 | /** 189 | * Register a new template function. 190 | * @param string $name; 191 | * @param callable $callback; 192 | * @return Engine 193 | */ 194 | public function registerFunction($name, $callback) 195 | { 196 | $this->functions->add($name, $callback); 197 | 198 | return $this; 199 | } 200 | 201 | /** 202 | * Remove a template function. 203 | * @param string $name; 204 | * @return Engine 205 | */ 206 | public function dropFunction($name) 207 | { 208 | $this->functions->remove($name); 209 | 210 | return $this; 211 | } 212 | 213 | /** 214 | * Get a template function. 215 | * @param string $name 216 | * @return Func 217 | */ 218 | public function getFunction($name) 219 | { 220 | return $this->functions->get($name); 221 | } 222 | 223 | /** 224 | * Check if a template function exists. 225 | * @param string $name 226 | * @return boolean 227 | */ 228 | public function doesFunctionExist($name) 229 | { 230 | return $this->functions->exists($name); 231 | } 232 | 233 | /** 234 | * Load an extension. 235 | * @param ExtensionInterface $extension 236 | * @return Engine 237 | */ 238 | public function loadExtension(ExtensionInterface $extension) 239 | { 240 | $extension->register($this); 241 | 242 | return $this; 243 | } 244 | 245 | /** 246 | * Load multiple extensions. 247 | * @param array $extensions 248 | * @return Engine 249 | */ 250 | public function loadExtensions(array $extensions = array()) 251 | { 252 | foreach ($extensions as $extension) { 253 | $this->loadExtension($extension); 254 | } 255 | 256 | return $this; 257 | } 258 | 259 | /** 260 | * Get a template path. 261 | * @param string $name 262 | * @return string 263 | */ 264 | public function path($name) 265 | { 266 | $name = new Name($this, $name); 267 | 268 | return $name->getPath(); 269 | } 270 | 271 | /** 272 | * Check if a template exists. 273 | * @param string $name 274 | * @return boolean 275 | */ 276 | public function exists($name) 277 | { 278 | $name = new Name($this, $name); 279 | 280 | return $name->doesPathExist(); 281 | } 282 | 283 | /** 284 | * Create a new template. 285 | * @param string $name 286 | * @param array $data 287 | * @return Template 288 | */ 289 | public function make($name, array $data = array()) 290 | { 291 | $template = new Template($this, $name); 292 | $template->data($data); 293 | return $template; 294 | } 295 | 296 | /** 297 | * Create a new template and render it. 298 | * @param string $name 299 | * @param array $data 300 | * @return string 301 | */ 302 | public function render($name, array $data = array()) 303 | { 304 | return $this->make($name)->render($data); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/Exception/TemplateNotFound.php: -------------------------------------------------------------------------------- 1 | template = $template; 12 | $this->paths = $paths; 13 | parent::__construct($message); 14 | } 15 | 16 | public function template(): string { 17 | return $this->template; 18 | } 19 | 20 | public function paths(): array { 21 | return $this->paths; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Extension/Asset.php: -------------------------------------------------------------------------------- 1 | path = rtrim($path, '/'); 40 | $this->filenameMethod = $filenameMethod; 41 | } 42 | 43 | /** 44 | * Register extension function. 45 | * @param Engine $engine 46 | * @return null 47 | */ 48 | public function register(Engine $engine) 49 | { 50 | $engine->registerFunction('asset', array($this, 'cachedAssetUrl')); 51 | } 52 | 53 | /** 54 | * Create "cache busted" asset URL. 55 | * @param string $url 56 | * @return string 57 | */ 58 | public function cachedAssetUrl($url) 59 | { 60 | $filePath = $this->path . '/' . ltrim($url, '/'); 61 | 62 | if (!file_exists($filePath)) { 63 | throw new LogicException( 64 | 'Unable to locate the asset "' . $url . '" in the "' . $this->path . '" directory.' 65 | ); 66 | } 67 | 68 | $lastUpdated = filemtime($filePath); 69 | $pathInfo = pathinfo($url); 70 | 71 | if ($pathInfo['dirname'] === '.') { 72 | $directory = ''; 73 | } elseif ($pathInfo['dirname'] === DIRECTORY_SEPARATOR) { 74 | $directory = '/'; 75 | } else { 76 | $directory = $pathInfo['dirname'] . '/'; 77 | } 78 | 79 | if ($this->filenameMethod) { 80 | return $directory . $pathInfo['filename'] . '.' . $lastUpdated . '.' . $pathInfo['extension']; 81 | } 82 | 83 | return $directory . $pathInfo['filename'] . '.' . $pathInfo['extension'] . '?v=' . $lastUpdated; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Extension/ExtensionInterface.php: -------------------------------------------------------------------------------- 1 | uri = $uri; 39 | $this->parts = explode('/', $this->uri); 40 | } 41 | 42 | /** 43 | * Register extension functions. 44 | * @param Engine $engine 45 | * @return null 46 | */ 47 | public function register(Engine $engine) 48 | { 49 | $engine->registerFunction('uri', array($this, 'runUri')); 50 | } 51 | 52 | /** 53 | * Perform URI check. 54 | * @param null|integer|string $var1 55 | * @param mixed $var2 56 | * @param mixed $var3 57 | * @param mixed $var4 58 | * @return mixed 59 | */ 60 | public function runUri($var1 = null, $var2 = null, $var3 = null, $var4 = null) 61 | { 62 | if (is_null($var1)) { 63 | return $this->uri; 64 | } 65 | 66 | if (is_numeric($var1) and is_null($var2)) { 67 | return array_key_exists($var1, $this->parts) ? $this->parts[$var1] : null; 68 | } 69 | 70 | if (is_numeric($var1) and is_string($var2)) { 71 | return $this->checkUriSegmentMatch($var1, $var2, $var3, $var4); 72 | } 73 | 74 | if (is_string($var1)) { 75 | return $this->checkUriRegexMatch($var1, $var2, $var3); 76 | } 77 | 78 | throw new LogicException('Invalid use of the uri function.'); 79 | } 80 | 81 | /** 82 | * Perform a URI segment match. 83 | * @param integer $key 84 | * @param string $string 85 | * @param mixed $returnOnTrue 86 | * @param mixed $returnOnFalse 87 | * @return mixed 88 | */ 89 | protected function checkUriSegmentMatch($key, $string, $returnOnTrue = null, $returnOnFalse = null) 90 | { 91 | if (array_key_exists($key, $this->parts) && $this->parts[$key] === $string) { 92 | return is_null($returnOnTrue) ? true : $returnOnTrue; 93 | } 94 | 95 | return is_null($returnOnFalse) ? false : $returnOnFalse; 96 | } 97 | 98 | /** 99 | * Perform a regular express match. 100 | * @param string $regex 101 | * @param mixed $returnOnTrue 102 | * @param mixed $returnOnFalse 103 | * @return mixed 104 | */ 105 | protected function checkUriRegexMatch($regex, $returnOnTrue = null, $returnOnFalse = null) 106 | { 107 | if (preg_match('#^' . $regex . '$#', $this->uri) === 1) { 108 | return is_null($returnOnTrue) ? true : $returnOnTrue; 109 | } 110 | 111 | return is_null($returnOnFalse) ? false : $returnOnFalse; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Template/Data.php: -------------------------------------------------------------------------------- 1 | shareWithAll($data); 34 | } 35 | 36 | if (is_array($templates)) { 37 | return $this->shareWithSome($data, $templates); 38 | } 39 | 40 | if (is_string($templates)) { 41 | return $this->shareWithSome($data, array($templates)); 42 | } 43 | 44 | throw new LogicException( 45 | 'The templates variable must be null, an array or a string, ' . gettype($templates) . ' given.' 46 | ); 47 | } 48 | 49 | /** 50 | * Add data shared with all templates. 51 | * @param array $data; 52 | * @return Data 53 | */ 54 | public function shareWithAll($data) 55 | { 56 | $this->sharedVariables = array_merge($this->sharedVariables, $data); 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Add data shared with some templates. 63 | * @param array $data; 64 | * @param array $templates; 65 | * @return Data 66 | */ 67 | public function shareWithSome($data, array $templates) 68 | { 69 | foreach ($templates as $template) { 70 | if (isset($this->templateVariables[$template])) { 71 | $this->templateVariables[$template] = array_merge($this->templateVariables[$template], $data); 72 | } else { 73 | $this->templateVariables[$template] = $data; 74 | } 75 | } 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * Get template data. 82 | * @param null|string $template; 83 | * @return array 84 | */ 85 | public function get($template = null) 86 | { 87 | if (isset($template, $this->templateVariables[$template])) { 88 | return array_merge($this->sharedVariables, $this->templateVariables[$template]); 89 | } 90 | 91 | return $this->sharedVariables; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Template/Directory.php: -------------------------------------------------------------------------------- 1 | set($path); 25 | } 26 | 27 | /** 28 | * Set path to templates directory. 29 | * @param string|null $path Pass null to disable the default directory. 30 | * @return Directory 31 | */ 32 | public function set($path) 33 | { 34 | if (!is_null($path) and !is_dir($path)) { 35 | throw new LogicException( 36 | 'The specified path "' . $path . '" does not exist.' 37 | ); 38 | } 39 | 40 | $this->path = $path; 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * Get path to templates directory. 47 | * @return string 48 | */ 49 | public function get() 50 | { 51 | return $this->path; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Template/FileExtension.php: -------------------------------------------------------------------------------- 1 | set($fileExtension); 23 | } 24 | 25 | /** 26 | * Set the template file extension. 27 | * @param null|string $fileExtension 28 | * @return FileExtension 29 | */ 30 | public function set($fileExtension) 31 | { 32 | $this->fileExtension = $fileExtension; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * Get the template file extension. 39 | * @return string 40 | */ 41 | public function get() 42 | { 43 | return $this->fileExtension; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Template/Folder.php: -------------------------------------------------------------------------------- 1 | setName($name); 39 | $this->setPath($path); 40 | $this->setFallback($fallback); 41 | } 42 | 43 | /** 44 | * Set the folder name. 45 | * @param string $name 46 | * @return Folder 47 | */ 48 | public function setName($name) 49 | { 50 | $this->name = $name; 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Get the folder name. 57 | * @return string 58 | */ 59 | public function getName() 60 | { 61 | return $this->name; 62 | } 63 | 64 | /** 65 | * Set the folder path. 66 | * @param string $path 67 | * @return Folder 68 | */ 69 | public function setPath($path) 70 | { 71 | if (!is_dir($path)) { 72 | throw new LogicException('The specified directory path "' . $path . '" does not exist.'); 73 | } 74 | 75 | $this->path = $path; 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * Get the folder path. 82 | * @return string 83 | */ 84 | public function getPath() 85 | { 86 | return $this->path; 87 | } 88 | 89 | /** 90 | * Set the folder fallback status. 91 | * @param boolean $fallback 92 | * @return Folder 93 | */ 94 | public function setFallback($fallback) 95 | { 96 | $this->fallback = $fallback; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Get the folder fallback status. 103 | * @return boolean 104 | */ 105 | public function getFallback() 106 | { 107 | return $this->fallback; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Template/Folders.php: -------------------------------------------------------------------------------- 1 | exists($name)) { 28 | throw new LogicException('The template folder "' . $name . '" is already being used.'); 29 | } 30 | 31 | $this->folders[$name] = new Folder($name, $path, $fallback); 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Remove a template folder. 38 | * @param string $name 39 | * @return Folders 40 | */ 41 | public function remove($name) 42 | { 43 | if (!$this->exists($name)) { 44 | throw new LogicException('The template folder "' . $name . '" was not found.'); 45 | } 46 | 47 | unset($this->folders[$name]); 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Get a template folder. 54 | * @param string $name 55 | * @return Folder 56 | */ 57 | public function get($name) 58 | { 59 | if (!$this->exists($name)) { 60 | throw new LogicException('The template folder "' . $name . '" was not found.'); 61 | } 62 | 63 | return $this->folders[$name]; 64 | } 65 | 66 | /** 67 | * Check if a template folder exists. 68 | * @param string $name 69 | * @return boolean 70 | */ 71 | public function exists($name) 72 | { 73 | return isset($this->folders[$name]); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Template/Func.php: -------------------------------------------------------------------------------- 1 | setName($name); 33 | $this->setCallback($callback); 34 | } 35 | 36 | /** 37 | * Set the function name. 38 | * @param string $name 39 | * @return Func 40 | */ 41 | public function setName($name) 42 | { 43 | if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $name) !== 1) { 44 | throw new LogicException( 45 | 'Not a valid function name.' 46 | ); 47 | } 48 | 49 | $this->name = $name; 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * Get the function name. 56 | * @return string 57 | */ 58 | public function getName() 59 | { 60 | return $this->name; 61 | } 62 | 63 | /** 64 | * Set the function callback 65 | * @param callable $callback 66 | * @return Func 67 | */ 68 | public function setCallback($callback) 69 | { 70 | if (!is_callable($callback, true)) { 71 | throw new LogicException( 72 | 'Not a valid function callback.' 73 | ); 74 | } 75 | 76 | $this->callback = $callback; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * Get the function callback. 83 | * @return callable 84 | */ 85 | public function getCallback() 86 | { 87 | return $this->callback; 88 | } 89 | 90 | /** 91 | * Call the function. 92 | * @param Template|null $template 93 | * @param array $arguments 94 | * @return mixed 95 | */ 96 | public function call(?Template $template = null, $arguments = array()) 97 | { 98 | if ( 99 | is_array($this->callback) && 100 | isset($this->callback[0]) && 101 | $this->callback[0] instanceof ExtensionInterface && 102 | property_exists($this->callback[0], 'template') 103 | ) { 104 | $this->callback[0]->template = $template; 105 | } 106 | 107 | return call_user_func_array($this->callback, $arguments); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Template/Functions.php: -------------------------------------------------------------------------------- 1 | exists($name)) { 27 | throw new LogicException( 28 | 'The template function name "' . $name . '" is already registered.' 29 | ); 30 | } 31 | 32 | $this->functions[$name] = new Func($name, $callback); 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * Remove a template function. 39 | * @param string $name; 40 | * @return Functions 41 | */ 42 | public function remove($name) 43 | { 44 | if (!$this->exists($name)) { 45 | throw new LogicException( 46 | 'The template function "' . $name . '" was not found.' 47 | ); 48 | } 49 | 50 | unset($this->functions[$name]); 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Get a template function. 57 | * @param string $name 58 | * @return Func 59 | */ 60 | public function get($name) 61 | { 62 | if (!$this->exists($name)) { 63 | throw new LogicException('The template function "' . $name . '" was not found.'); 64 | } 65 | 66 | return $this->functions[$name]; 67 | } 68 | 69 | /** 70 | * Check if a template function exists. 71 | * @param string $name 72 | * @return boolean 73 | */ 74 | public function exists($name) 75 | { 76 | return isset($this->functions[$name]); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Template/Name.php: -------------------------------------------------------------------------------- 1 | setEngine($engine); 45 | $this->setName($name); 46 | } 47 | 48 | /** 49 | * Set the engine. 50 | * @param Engine $engine 51 | * @return Name 52 | */ 53 | public function setEngine(Engine $engine) 54 | { 55 | $this->engine = $engine; 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * Get the engine. 62 | * @return Engine 63 | */ 64 | public function getEngine() 65 | { 66 | return $this->engine; 67 | } 68 | 69 | /** 70 | * Set the original name and parse it. 71 | * @param string $name 72 | * @return Name 73 | */ 74 | public function setName($name) 75 | { 76 | $this->name = $name; 77 | 78 | $parts = explode('::', $this->name); 79 | 80 | if (count($parts) === 1) { 81 | $this->setFile($parts[0]); 82 | } elseif (count($parts) === 2) { 83 | $this->setFolder($parts[0]); 84 | $this->setFile($parts[1]); 85 | } else { 86 | throw new LogicException( 87 | 'The template name "' . $this->name . '" is not valid. ' . 88 | 'Do not use the folder namespace separator "::" more than once.' 89 | ); 90 | } 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * Get the original name. 97 | * @return string 98 | */ 99 | public function getName() 100 | { 101 | return $this->name; 102 | } 103 | 104 | /** 105 | * Set the parsed template folder. 106 | * @param string $folder 107 | * @return Name 108 | */ 109 | public function setFolder($folder) 110 | { 111 | $this->folder = $this->engine->getFolders()->get($folder); 112 | 113 | return $this; 114 | } 115 | 116 | /** 117 | * Get the parsed template folder. 118 | * @return string 119 | */ 120 | public function getFolder() 121 | { 122 | return $this->folder; 123 | } 124 | 125 | /** 126 | * Set the parsed template file. 127 | * @param string $file 128 | * @return Name 129 | */ 130 | public function setFile($file) 131 | { 132 | if ($file === '') { 133 | throw new LogicException( 134 | 'The template name "' . $this->name . '" is not valid. ' . 135 | 'The template name cannot be empty.' 136 | ); 137 | } 138 | 139 | $this->file = $file; 140 | 141 | if (!is_null($this->engine->getFileExtension())) { 142 | $this->file .= '.' . $this->engine->getFileExtension(); 143 | } 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Get the parsed template file. 150 | * @return string 151 | */ 152 | public function getFile() 153 | { 154 | return $this->file; 155 | } 156 | 157 | /** 158 | * Resolve template path. 159 | * @return string 160 | */ 161 | public function getPath() 162 | { 163 | if (is_null($this->folder)) { 164 | return "{$this->getDefaultDirectory()}/{$this->file}"; 165 | } 166 | 167 | $path = "{$this->folder->getPath()}/{$this->file}"; 168 | 169 | if ( 170 | !is_file($path) 171 | && $this->folder->getFallback() 172 | && is_file("{$this->getDefaultDirectory()}/{$this->file}") 173 | ) { 174 | $path = "{$this->getDefaultDirectory()}/{$this->file}"; 175 | } 176 | 177 | return $path; 178 | } 179 | 180 | /** 181 | * Check if template path exists. 182 | * @return boolean 183 | */ 184 | public function doesPathExist() 185 | { 186 | return is_file($this->getPath()); 187 | } 188 | 189 | /** 190 | * Get the default templates directory. 191 | * @return string 192 | */ 193 | protected function getDefaultDirectory() 194 | { 195 | $directory = $this->engine->getDirectory(); 196 | 197 | if (is_null($directory)) { 198 | throw new LogicException( 199 | 'The template name "' . $this->name . '" is not valid. '. 200 | 'The default directory has not been defined.' 201 | ); 202 | } 203 | 204 | return $directory; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/Template/ResolveTemplatePath.php: -------------------------------------------------------------------------------- 1 | getPath(); 14 | if (is_file($path)) { 15 | return $path; 16 | } 17 | 18 | throw new TemplateNotFound($name->getName(), [$name->getPath()], 'The template "' . $name->getName() . '" could not be found at "' . $name->getPath() . '".'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Template/ResolveTemplatePath/ThemeResolveTemplatePath.php: -------------------------------------------------------------------------------- 1 | theme = $theme; 16 | } 17 | 18 | public function __invoke(Name $name): string { 19 | $searchedPaths = []; 20 | foreach ($this->theme->listThemeHierarchy() as $theme) { 21 | $path = $theme->dir() . '/' . $name->getName() . '.' . $name->getEngine()->getFileExtension(); 22 | if (is_file($path)) { 23 | return $path; 24 | } 25 | $searchedPaths[] = [$theme->name(), $path]; 26 | } 27 | 28 | throw new TemplateNotFound( 29 | $name->getName(), 30 | array_map(function(array $tup) { 31 | return $tup[1]; 32 | }, $searchedPaths), 33 | sprintf('The template "%s" was not found in the following themes: %s', 34 | $name->getName(), 35 | implode(', ', array_map(function(array $tup) { 36 | return implode(':', $tup); 37 | }, $searchedPaths)) 38 | ) 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Template/Template.php: -------------------------------------------------------------------------------- 1 | engine = $engine; 83 | $this->name = new Name($engine, $name); 84 | 85 | $this->data($this->engine->getData($name)); 86 | } 87 | 88 | /** 89 | * Magic method used to call extension functions. 90 | * @param string $name 91 | * @param array $arguments 92 | * @return mixed 93 | */ 94 | public function __call($name, $arguments) 95 | { 96 | return $this->engine->getFunction($name)->call($this, $arguments); 97 | } 98 | 99 | /** 100 | * Alias for render() method. 101 | * @throws \Throwable 102 | * @throws \Exception 103 | * @return string 104 | */ 105 | public function __toString() 106 | { 107 | return $this->render(); 108 | } 109 | 110 | /** 111 | * Assign or get template data. 112 | * @param array|null $data 113 | * @return array|void 114 | */ 115 | public function data(?array $data = null) 116 | { 117 | if (is_null($data)) { 118 | return $this->data; 119 | } 120 | 121 | $this->data = array_merge($this->data, $data); 122 | } 123 | 124 | /** 125 | * Check if the template exists. 126 | * @return boolean 127 | */ 128 | public function exists() 129 | { 130 | try { 131 | ($this->engine->getResolveTemplatePath())($this->name); 132 | return true; 133 | } catch (TemplateNotFound $e) { 134 | return false; 135 | } 136 | } 137 | 138 | /** 139 | * Get the template path. 140 | * @return string 141 | */ 142 | public function path() 143 | { 144 | try { 145 | return ($this->engine->getResolveTemplatePath())($this->name); 146 | } catch (TemplateNotFound $e) { 147 | return $e->paths()[0]; 148 | } 149 | } 150 | 151 | /** 152 | * Render the template and layout. 153 | * @param array $data 154 | * @throws \Throwable 155 | * @throws \Exception 156 | * @return string 157 | */ 158 | public function render(array $data = array()) 159 | { 160 | $this->data($data); 161 | $path = ($this->engine->getResolveTemplatePath())($this->name); 162 | 163 | try { 164 | $level = ob_get_level(); 165 | ob_start(); 166 | 167 | (function() { 168 | extract($this->data); 169 | include func_get_arg(0); 170 | })($path); 171 | 172 | $content = ob_get_clean(); 173 | 174 | if (isset($this->layoutName)) { 175 | $layout = $this->engine->make($this->layoutName); 176 | $layout->sections = array_merge($this->sections, array('content' => $content)); 177 | $content = $layout->render($this->layoutData); 178 | } 179 | 180 | return $content; 181 | } catch (Throwable $e) { 182 | while (ob_get_level() > $level) { 183 | ob_end_clean(); 184 | } 185 | 186 | throw $e; 187 | } 188 | } 189 | 190 | /** 191 | * Set the template's layout. 192 | * @param string $name 193 | * @param array $data 194 | * @return null 195 | */ 196 | public function layout($name, array $data = array()) 197 | { 198 | $this->layoutName = $name; 199 | $this->layoutData = $data; 200 | } 201 | 202 | /** 203 | * Start a new section block. 204 | * @param string $name 205 | * @return null 206 | */ 207 | public function start($name) 208 | { 209 | if ($name === 'content') { 210 | throw new LogicException( 211 | 'The section name "content" is reserved.' 212 | ); 213 | } 214 | 215 | if ($this->sectionName) { 216 | throw new LogicException('You cannot nest sections within other sections.'); 217 | } 218 | 219 | $this->sectionName = $name; 220 | 221 | ob_start(); 222 | } 223 | 224 | /** 225 | * Start a new section block in APPEND mode. 226 | * @param string $name 227 | * @return null 228 | */ 229 | public function push($name) 230 | { 231 | $this->appendSection = true; /* for backward compatibility */ 232 | $this->sectionMode = self::SECTION_MODE_APPEND; 233 | $this->start($name); 234 | } 235 | 236 | /** 237 | * Start a new section block in PREPEND mode. 238 | * @param string $name 239 | * @return null 240 | */ 241 | public function unshift($name) 242 | { 243 | $this->appendSection = false; /* for backward compatibility */ 244 | $this->sectionMode = self::SECTION_MODE_PREPEND; 245 | $this->start($name); 246 | } 247 | 248 | /** 249 | * Stop the current section block. 250 | * @return null 251 | */ 252 | public function stop() 253 | { 254 | if (is_null($this->sectionName)) { 255 | throw new LogicException( 256 | 'You must start a section before you can stop it.' 257 | ); 258 | } 259 | 260 | if (!isset($this->sections[$this->sectionName])) { 261 | $this->sections[$this->sectionName] = ''; 262 | } 263 | 264 | switch ($this->sectionMode) { 265 | 266 | case self::SECTION_MODE_REWRITE: 267 | $this->sections[$this->sectionName] = ob_get_clean(); 268 | break; 269 | 270 | case self::SECTION_MODE_APPEND: 271 | $this->sections[$this->sectionName] .= ob_get_clean(); 272 | break; 273 | 274 | case self::SECTION_MODE_PREPEND: 275 | $this->sections[$this->sectionName] = ob_get_clean().$this->sections[$this->sectionName]; 276 | break; 277 | 278 | } 279 | $this->sectionName = null; 280 | $this->sectionMode = self::SECTION_MODE_REWRITE; 281 | $this->appendSection = false; /* for backward compatibility */ 282 | } 283 | 284 | /** 285 | * Alias of stop(). 286 | * @return null 287 | */ 288 | public function end() 289 | { 290 | $this->stop(); 291 | } 292 | 293 | /** 294 | * Returns the content for a section block. 295 | * @param string $name Section name 296 | * @param string $default Default section content 297 | * @return string|null 298 | */ 299 | public function section($name, $default = null) 300 | { 301 | if (!isset($this->sections[$name])) { 302 | return $default; 303 | } 304 | 305 | return $this->sections[$name]; 306 | } 307 | 308 | /** 309 | * Fetch a rendered template. 310 | * @param string $name 311 | * @param array $data 312 | * @return string 313 | */ 314 | public function fetch($name, array $data = array()) 315 | { 316 | return $this->engine->render($name, $data); 317 | } 318 | 319 | /** 320 | * Output a rendered template. 321 | * @param string $name 322 | * @param array $data 323 | * @return null 324 | */ 325 | public function insert($name, array $data = array()) 326 | { 327 | echo $this->engine->render($name, $data); 328 | } 329 | 330 | /** 331 | * Apply multiple functions to variable. 332 | * @param mixed $var 333 | * @param string $functions 334 | * @return mixed 335 | */ 336 | public function batch($var, $functions) 337 | { 338 | foreach (explode('|', $functions) as $function) { 339 | if ($this->engine->doesFunctionExist($function)) { 340 | $var = call_user_func(array($this, $function), $var); 341 | } elseif (is_callable($function)) { 342 | $var = call_user_func($function, $var); 343 | } else { 344 | throw new LogicException( 345 | 'The batch function could not find the "' . $function . '" function.' 346 | ); 347 | } 348 | } 349 | 350 | return $var; 351 | } 352 | 353 | /** 354 | * Escape string. 355 | * @param string $string 356 | * @param null|string $functions 357 | * @return string 358 | */ 359 | public function escape($string, $functions = null) 360 | { 361 | static $flags; 362 | 363 | if (!isset($flags)) { 364 | $flags = ENT_QUOTES | ENT_SUBSTITUTE; 365 | } 366 | 367 | if ($functions) { 368 | $string = $this->batch($string, $functions); 369 | } 370 | 371 | return htmlspecialchars($string ?? '', $flags, 'UTF-8'); 372 | } 373 | 374 | /** 375 | * Alias to escape function. 376 | * @param string $string 377 | * @param null|string $functions 378 | * @return string 379 | */ 380 | public function e($string, $functions = null) 381 | { 382 | return $this->escape($string, $functions); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /src/Template/Theme.php: -------------------------------------------------------------------------------- 1 | dir = $dir; 14 | $this->name = $name; 15 | } 16 | 17 | /** @param Theme[] $themes */ 18 | public static function hierarchy(array $themes): Theme { 19 | self::assertThemesForHierarchyAreNotEmpty($themes); 20 | self::assertAllThemesInHierarchyAreLeafThemes($themes); 21 | 22 | /** @var Theme $theme */ 23 | $theme = array_reduce(array_slice($themes, 1), function(Theme $parent, Theme $child) { 24 | $child->next = $parent; 25 | return $child; 26 | }, $themes[0]); 27 | self::assertHierarchyContainsUniqueThemeNames($theme); 28 | return $theme; 29 | } 30 | 31 | public static function new(string $dir, string $name = 'Default'): self { 32 | return new self($dir, $name); 33 | } 34 | 35 | public function dir(): string { 36 | return $this->dir; 37 | } 38 | 39 | public function name(): string { 40 | return $this->name; 41 | } 42 | 43 | /** 44 | * list all directories in the hierarchy from first to last 45 | * @return Theme[] 46 | */ 47 | public function listThemeHierarchy(): \Generator { 48 | yield $this; 49 | if ($this->next) { 50 | yield from $this->next->listThemeHierarchy(); 51 | } 52 | } 53 | 54 | /** @param Theme[] $themes */ 55 | private static function assertThemesForHierarchyAreNotEmpty(array $themes) { 56 | if (count($themes) === 0) { 57 | throw new \RuntimeException('Empty theme hierarchies are not allowed.'); 58 | } 59 | } 60 | 61 | /** @param Theme[] $themes */ 62 | private static function assertAllThemesInHierarchyAreLeafThemes(array $themes) { 63 | foreach ($themes as $theme) { 64 | if ($theme->next) { 65 | throw new \RuntimeException('Nested theme hierarchies are not allowed, make sure to use Theme::new when creating themes in your hierarchy. Theme ' . $theme->name . ' is already in a hierarchy.'); 66 | } 67 | } 68 | } 69 | 70 | private static function assertHierarchyContainsUniqueThemeNames(Theme $theme) { 71 | $names = []; 72 | foreach ($theme->listThemeHierarchy() as $theme) { 73 | $names[] = $theme->name; 74 | } 75 | 76 | if (count(array_unique($names)) !== count($names)) { 77 | throw new \RuntimeException('Duplicate theme names in hierarchies are not allowed. Received theme names: [' . implode(', ', $names) . '].'); 78 | } 79 | } 80 | } 81 | --------------------------------------------------------------------------------