├── .editorconfig ├── .github └── workflows │ └── run-tests.yml ├── .gitignore ├── .styleci.yml ├── .travis.yml ├── CHANGELOG.md ├── CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Compilers │ ├── Shortcode.php │ └── ShortcodeCompiler.php ├── Facades │ └── Shortcode.php ├── Shortcode.php ├── ShortcodesServiceProvider.php └── View │ ├── Factory.php │ └── View.php └── tests ├── NamespaceTest.php ├── ShortcodeTest.php ├── TestCase.php └── views └── test.blade.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | php: [7.2, 7.3, 7.4, 8.0, 8.1, '8.2', '8.3', '8.4'] 15 | dependency-version: [prefer-lowest, prefer-stable] 16 | 17 | name: P${{ matrix.php }} - ${{ matrix.dependency-version }} 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v2 22 | 23 | - name: Setup PHP 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: ${{ matrix.php }} 27 | extensions: mbstring 28 | coverage: none 29 | 30 | - name: Get composer cache directory 31 | id: composer-cache 32 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 33 | 34 | - name: Cache composer dependencies 35 | uses: actions/cache@v2 36 | with: 37 | path: ${{ steps.composer-cache.outputs.dir }} 38 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} 39 | restore-keys: ${{ runner.os }}-composer- 40 | 41 | - name: Install dependencies 42 | run: | 43 | composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader 44 | 45 | - name: Execute tests 46 | run: vendor/bin/phpunit --no-coverage 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .phpunit.result.cache 3 | composer.lock 4 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.2 5 | 6 | before_script: 7 | - composer self-update 8 | - composer install --prefer-source --no-interaction 9 | 10 | script: phpunit 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All Notable changes to `laravel-shortcodes` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://github.com/webwizo/laravel-shortcodes) principles. 6 | 7 | ## 2016-12-06 8 | 9 | #### Shortcodes Latest version: v1.0.6 10 | 11 | ### Added 12 | - Add Laravel versions from ~5.1 to ~5.3 13 | 14 | ## 2016-08-25 15 | 16 | #### Shortcodes Latest version: v1.0.5 17 | 18 | ### Added 19 | - Update dependencies for Laravel 5.3 20 | 21 | 22 | ## 2016-05-26 23 | 24 | ### Added 25 | - Strip shortcodes 26 | 27 | 28 | ## 2016-05-24 29 | 30 | ### Added 31 | - Compile Shortcodes 32 | 33 | 34 | -------------------------------------------------------------------------------- /CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | * The use of sexualized language or imagery 10 | * Personal attacks 11 | * Trolling or insulting/derogatory comments 12 | * Public or private harassment 13 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission 14 | * Other unethical or unprofessional conduct. 15 | 16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 17 | 18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community in a direct capacity. Personal views, beliefs and values of individuals do not necessarily reflect those of the organisation or affiliated individuals and organisations. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 21 | 22 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 23 | -------------------------------------------------------------------------------- /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/webwizo/laravel-shortcodes). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[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). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **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](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Asif Iqbal 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel-Shortcodes 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](LICENSE.md) 5 | [![Build Status][ico-travis]][link-travis] 6 | [![Coverage Status][ico-scrutinizer]][link-scrutinizer] 7 | [![Quality Score][ico-code-quality]][link-code-quality] 8 | [![Total Downloads][ico-downloads]][link-downloads] 9 | [![StyleCI](https://styleci.io/repos/59507292/shield)](https://styleci.io/repos/59507292) 10 | 11 | WordPress like shortcodes for Laravel 5.x 12 | 13 | ```php 14 | [b class="bold"]Bold text[/b] 15 | 16 | [tabs] 17 | [tab]Tab 1[/tab] 18 | [tab]Tab 2[/tab] 19 | [/tabs] 20 | 21 | [user id="1" display="name"] 22 | ``` 23 | 24 | If you are looking for Laravel 4.2, see: https://github.com/patrickbrouwers/Laravel-Shortcodes 25 | 26 | ## Install 27 | 28 | Via Composer 29 | 30 | ``` bash 31 | $ composer require "webwizo/laravel-shortcodes:1.0.*" 32 | ``` 33 | 34 | After updating composer, add the ServiceProvider to the providers array in `config/app.php` 35 | 36 | ## Usage 37 | 38 | ```php 39 | Webwizo\Shortcodes\ShortcodesServiceProvider::class, 40 | ``` 41 | 42 | You can use the facade for shorter code. Add this to your aliases: 43 | 44 | ```php 45 | 'Shortcode' => Webwizo\Shortcodes\Facades\Shortcode::class, 46 | ``` 47 | 48 | The class is bound to the ioC as `shortcode` 49 | 50 | ```php 51 | $shortcode = app('shortcode'); 52 | ``` 53 | 54 | # Usage 55 | 56 | ### withShortcodes() 57 | 58 | To enable the view compiling features: 59 | 60 | ```php 61 | return view('view')->withShortcodes(); 62 | ``` 63 | 64 | This will enable shortcode rendering for that view only. 65 | 66 | ### Enable through class 67 | 68 | ```php 69 | Shortcode::enable(); 70 | ``` 71 | 72 | ### Disable through class 73 | 74 | ```php 75 | Shortcode::disable(); 76 | ``` 77 | 78 | ### Disabling some views from shortcode compiling 79 | 80 | With the config set to true, you can disable the compiling per view. 81 | 82 | ```php 83 | return view('view')->withoutShortcodes(); 84 | ``` 85 | 86 | ## Default compiling 87 | 88 | To use default compiling: 89 | 90 | ```php 91 | Shortcode::compile($contents); 92 | ``` 93 | 94 | ### Strip shortcodes from rendered view. 95 | 96 | ```php 97 | return view('view')->withStripShortcodes(); 98 | ``` 99 | 100 | ## Strip shortcode through class 101 | 102 | ```php 103 | Shortcode::strip($contents); 104 | ``` 105 | 106 | ## Registering new shortcodes 107 | 108 | Create a new ServiceProvider where you can register all the shortcodes. 109 | 110 | ``` bash 111 | php artisan make:provider ShortcodesServiceProvider 112 | ``` 113 | 114 | After defining shortcodes, add the ServiceProvider to the providers array in `config/app.php` 115 | 116 | ## Usage 117 | 118 | ```php 119 | App\Providers\ShortcodesServiceProvider::class, 120 | ``` 121 | 122 | ### Callback 123 | 124 | Shortcodes can be registered within ShortcodesServiceProvider with a callback: 125 | 126 | ```bash 127 | php artisan make:provider ShortcodesServiceProvider 128 | ``` 129 | 130 | ShortcodesServiceProvider.php Class File 131 | ```php 132 | %s', $shortcode->class, $content); 174 | } 175 | 176 | } 177 | ``` 178 | 179 | ### Class with custom method 180 | 181 | You can store each shortcode within their class `app/Shortcodes/ItalicShortcode.php` 182 | ```php 183 | namespace App\Shortcodes; 184 | 185 | class ItalicShortcode { 186 | 187 | public function custom($shortcode, $content, $compiler, $name, $viewData) 188 | { 189 | return sprintf('%s', $shortcode->class, $content); 190 | } 191 | 192 | } 193 | ``` 194 | 195 | ### Register helpers 196 | 197 | If you only want to show the html attribute when the attribute is provided in the shortcode, you can use `$shortcode->get($attributeKey, $fallbackValue = null)` 198 | 199 | ```php 200 | class BoldShortcode { 201 | 202 | public function register($shortcode, $content, $compiler, $name, $viewData) 203 | { 204 | return 'get('class', 'default') .'>' . $content . ''; 205 | } 206 | 207 | } 208 | ``` 209 | 210 | ## Change log 211 | 212 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 213 | 214 | ## Contributing 215 | 216 | Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details. 217 | 218 | ## Security 219 | 220 | If you discover any security related issues, please email webwizo@gmail.com instead of using the issue tracker. 221 | 222 | ## Credits 223 | 224 | - [Asif Iqbal][link-author] 225 | - [All Contributors][link-contributors] 226 | 227 | ## Support me 228 | 229 | Buy Me A Coffee 230 | 231 | ## License 232 | 233 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 234 | 235 | [ico-version]: https://img.shields.io/packagist/v/webwizo/laravel-shortcodes.svg?style=flat-square 236 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 237 | [ico-travis]: https://img.shields.io/travis/webwizo/laravel-shortcodes/master.svg?style=flat-square 238 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/webwizo/laravel-shortcodes.svg?style=flat-square 239 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/webwizo/laravel-shortcodes.svg?style=flat-square 240 | [ico-downloads]: https://img.shields.io/packagist/dt/webwizo/laravel-shortcodes.svg?style=flat-square 241 | 242 | [link-packagist]: https://packagist.org/packages/webwizo/laravel-shortcodes 243 | [link-travis]: https://travis-ci.org/webwizo/laravel-shortcodes 244 | [link-scrutinizer]: https://scrutinizer-ci.com/g/webwizo/laravel-shortcodes/code-structure 245 | [link-code-quality]: https://scrutinizer-ci.com/g/webwizo/laravel-shortcodes 246 | [link-downloads]: https://packagist.org/packages/webwizo/laravel-shortcodes 247 | [link-author]: https://github.com/webwizo 248 | [link-contributors]: ../../contributors 249 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webwizo/laravel-shortcodes", 3 | "type": "library", 4 | "description": "Wordpress like shortcodes for Laravel 5, 6, 7, 8, 9, 10 and 11", 5 | "keywords": [ 6 | "laravel", 7 | "wordpress", 8 | "shortcodes" 9 | ], 10 | "homepage": "https://github.com/webwizo/laravel-shortcodes", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Asif Iqbal", 15 | "email": "webwizo@gmail.com", 16 | "homepage": "http://www.webwizo.com", 17 | "role": "Developer" 18 | } 19 | ], 20 | "require": { 21 | "illuminate/view": "5.6.x|5.7.x|5.8.x|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", 22 | "illuminate/support": "5.6.x|5.7.x|5.8.x|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", 23 | "illuminate/contracts": "5.6.x|5.7.x|5.8.x|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", 24 | "php": "^7.2|^8.0|^8.1|^8.2" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^8.0|^9.0|^11.5.3", 28 | "orchestra/testbench": "~3.9.0|^4.0|^5.0|^6.0|^7.0|^8.0|^10.0", 29 | "scrutinizer/ocular": "^1.5", 30 | "squizlabs/php_codesniffer": "~2.3|^3.7" 31 | }, 32 | "autoload": { 33 | "classmap": [ 34 | "tests/TestCase.php" 35 | ], 36 | "psr-4": { 37 | "Webwizo\\Shortcodes\\": "src" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "Webwizo\\Shortcodes\\": "tests" 43 | } 44 | }, 45 | "scripts": { 46 | "test": "phpunit" 47 | }, 48 | "extra": { 49 | "laravel": { 50 | "providers": [ 51 | "Webwizo\\Shortcodes\\ShortcodesServiceProvider" 52 | ], 53 | "aliases": { 54 | "Shortcode": "Webwizo\\Shortcodes\\Facades\\Shortcode" 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Compilers/Shortcode.php: -------------------------------------------------------------------------------- 1 | name = $name; 38 | $this->content = $content; 39 | $this->attributes = $attributes; 40 | } 41 | 42 | /** 43 | * Get html attribute 44 | * 45 | * @param string $attribute 46 | * 47 | * @return string|null 48 | */ 49 | public function get($attribute, $fallback = null) 50 | { 51 | $value = $this->{$attribute}; 52 | if (!is_null($value)) { 53 | return $attribute . '="' . $value . '"'; 54 | } elseif (!is_null($fallback)) { 55 | return $attribute . '="' . $fallback . '"'; 56 | } 57 | } 58 | 59 | /** 60 | * Get shortcode name 61 | * 62 | * @return string 63 | */ 64 | public function getName() 65 | { 66 | return $this->name; 67 | } 68 | 69 | /** 70 | * Get shortcode attributes 71 | * 72 | * @return string 73 | */ 74 | public function getContent() 75 | { 76 | return $this->content; 77 | } 78 | 79 | /** 80 | * Return array of attributes; 81 | * 82 | * @return array 83 | */ 84 | public function toArray() 85 | { 86 | return $this->attributes; 87 | } 88 | 89 | /** 90 | * Dynamically get attributes 91 | * 92 | * @param string $param 93 | * 94 | * @return string|null 95 | */ 96 | public function __get($param) 97 | { 98 | return isset($this->attributes[$param]) ? $this->attributes[$param] : null; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Compilers/ShortcodeCompiler.php: -------------------------------------------------------------------------------- 1 | enabled = true; 51 | } 52 | 53 | /** 54 | * Disable 55 | * 56 | * @return void 57 | */ 58 | public function disable() 59 | { 60 | $this->enabled = false; 61 | } 62 | 63 | /** 64 | * Add a new shortcode 65 | * 66 | * @param string $name 67 | * @param callable|string $callback 68 | */ 69 | public function add($name, $callback) 70 | { 71 | $this->registered[$name] = $callback; 72 | } 73 | 74 | public function attachData($data) 75 | { 76 | $this->data = $data; 77 | } 78 | 79 | /** 80 | * Compile the contents 81 | * 82 | * @param string $value 83 | * 84 | * @return string 85 | */ 86 | public function compile($value) 87 | { 88 | // Only continue is laravel-shortcodes have been registered 89 | if (!$this->enabled || !$this->hasShortcodes()) { 90 | return $value; 91 | } 92 | // Set empty result 93 | $result = ''; 94 | // Here we will loop through all of the tokens returned by the Zend lexer and 95 | // parse each one into the corresponding valid PHP. We will then have this 96 | // template as the correctly rendered PHP that can be rendered natively. 97 | foreach (token_get_all($value) as $token) { 98 | $result .= is_array($token) ? $this->parseToken($token) : $token; 99 | } 100 | 101 | return $result; 102 | } 103 | 104 | /** 105 | * Check if laravel-shortcodes have been registered 106 | * 107 | * @return boolean 108 | */ 109 | public function hasShortcodes() 110 | { 111 | return !empty($this->registered); 112 | } 113 | 114 | /** 115 | * Parse the tokens from the template. 116 | * 117 | * @param array $token 118 | * 119 | * @return string 120 | */ 121 | protected function parseToken($token) 122 | { 123 | list($id, $content) = $token; 124 | if ($id == T_INLINE_HTML) { 125 | $content = $this->renderShortcodes($content); 126 | } 127 | 128 | return $content; 129 | } 130 | 131 | /** 132 | * Render laravel-shortcodes 133 | * 134 | * @param string $value 135 | * 136 | * @return string 137 | */ 138 | protected function renderShortcodes($value) 139 | { 140 | $pattern = $this->getRegex(); 141 | 142 | return preg_replace_callback("/{$pattern}/s", [$this, 'render'], $value); 143 | } 144 | 145 | // get view data 146 | public function viewData( $viewData ) 147 | { 148 | $this->_viewData = $viewData; 149 | return $this; 150 | } 151 | 152 | /** 153 | * Render the current calld shortcode. 154 | * 155 | * @param array $matches 156 | * 157 | * @return string 158 | */ 159 | public function render($matches) 160 | { 161 | // Compile the shortcode 162 | $compiled = $this->compileShortcode($matches); 163 | $name = $compiled->getName(); 164 | $viewData = $this->_viewData; 165 | 166 | // Render the shortcode through the callback 167 | return call_user_func_array($this->getCallback($name), [ 168 | $compiled, 169 | $compiled->getContent(), 170 | $this, 171 | $name, 172 | $viewData 173 | ]); 174 | } 175 | 176 | /** 177 | * Get Compiled Attributes. 178 | * 179 | * @param $matches 180 | * 181 | * @return \Webwizo\Shortcodes\Shortcode 182 | */ 183 | protected function compileShortcode($matches) 184 | { 185 | // Set matches 186 | $this->setMatches($matches); 187 | // pars the attributes 188 | $attributes = $this->parseAttributes($this->matches[3]); 189 | 190 | // return shortcode instance 191 | return new Shortcode( 192 | $this->getName(), 193 | $this->getContent(), 194 | $attributes 195 | ); 196 | } 197 | 198 | /** 199 | * Set the matches 200 | * 201 | * @param array $matches 202 | */ 203 | protected function setMatches($matches = []) 204 | { 205 | $this->matches = $matches; 206 | } 207 | 208 | /** 209 | * Return the shortcode name 210 | * 211 | * @return string 212 | */ 213 | public function getName() 214 | { 215 | return $this->matches[2]; 216 | } 217 | 218 | /** 219 | * Return the shortcode content 220 | * 221 | * @return string 222 | */ 223 | public function getContent() 224 | { 225 | // Compile the content, to support nested laravel-shortcodes 226 | return $this->compile($this->matches[5]); 227 | } 228 | 229 | /** 230 | * Return the view data 231 | * 232 | * @return array 233 | */ 234 | public function getData() 235 | { 236 | return $this->data; 237 | } 238 | 239 | 240 | /** 241 | * Get the callback for the current shortcode (class or callback) 242 | * 243 | * @param string $name 244 | * 245 | * @return callable|array 246 | */ 247 | public function getCallback($name) 248 | { 249 | // Get the callback from the laravel-shortcodes array 250 | $callback = $this->registered[$name]; 251 | // if is a string 252 | if (is_string($callback)) { 253 | // Parse the callback 254 | list($class, $method) = Str::parseCallback($callback, 'register'); 255 | // If the class exist 256 | if (class_exists($class)) { 257 | // return class and method 258 | return [ 259 | app($class), 260 | $method 261 | ]; 262 | } 263 | } 264 | 265 | return $callback; 266 | } 267 | 268 | /** 269 | * Parse the shortcode attributes 270 | * 271 | * @author Wordpress 272 | * @return array 273 | */ 274 | protected function parseAttributes($text) 275 | { 276 | // decode attribute values 277 | $text = htmlspecialchars_decode($text, ENT_QUOTES); 278 | 279 | $attributes = []; 280 | // attributes pattern 281 | $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; 282 | // Match 283 | if (preg_match_all($pattern, preg_replace('/[\x{00a0}\x{200b}]+/u', " ", $text), $match, PREG_SET_ORDER)) { 284 | foreach ($match as $m) { 285 | if (!empty($m[1])) { 286 | $attributes[strtolower($m[1])] = stripcslashes($m[2]); 287 | } elseif (!empty($m[3])) { 288 | $attributes[strtolower($m[3])] = stripcslashes($m[4]); 289 | } elseif (!empty($m[5])) { 290 | $attributes[strtolower($m[5])] = stripcslashes($m[6]); 291 | } elseif (isset($m[7]) && strlen($m[7])) { 292 | $attributes[] = stripcslashes($m[7]); 293 | } elseif (isset($m[8])) { 294 | $attributes[] = stripcslashes($m[8]); 295 | } 296 | } 297 | } else { 298 | $attributes = ltrim($text); 299 | } 300 | 301 | // return attributes 302 | return is_array($attributes) ? $attributes : [$attributes]; 303 | } 304 | 305 | /** 306 | * Get shortcode names 307 | * 308 | * @return string 309 | */ 310 | protected function getShortcodeNames() 311 | { 312 | return join('|', array_map('preg_quote', array_keys($this->registered))); 313 | } 314 | 315 | /** 316 | * Get shortcode regex. 317 | * 318 | * @author Wordpress 319 | * @return string 320 | */ 321 | protected function getRegex() 322 | { 323 | $shortcodeNames = $this->getShortcodeNames(); 324 | 325 | return "\\[(\\[?)($shortcodeNames)(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*+(?:\\[(?!\\/\\2\\])[^\\[]*+)*+)\\[\\/\\2\\])?)(\\]?)"; 326 | } 327 | 328 | /** 329 | * Remove all shortcode tags from the given content. 330 | * 331 | * @param string $content Content to remove shortcode tags. 332 | * 333 | * @return string Content without shortcode tags. 334 | */ 335 | public function strip($content) 336 | { 337 | if (empty($this->registered)) { 338 | return $content; 339 | } 340 | $pattern = $this->getRegex(); 341 | 342 | return preg_replace_callback("/{$pattern}/s", [$this, 'stripTag'], $content); 343 | } 344 | 345 | /** 346 | * @return boolean 347 | */ 348 | public function getStrip() 349 | { 350 | return $this->strip; 351 | } 352 | 353 | /** 354 | * @param boolean $strip 355 | */ 356 | public function setStrip($strip) 357 | { 358 | $this->strip = $strip; 359 | } 360 | 361 | /** 362 | * Remove shortcode tag 363 | * 364 | * @param type $m 365 | * 366 | * @return string Content without shortcode tag. 367 | */ 368 | protected function stripTag($m) 369 | { 370 | if ($m[1] == '[' && $m[6] == ']') { 371 | return substr($m[0], 1, -1); 372 | } 373 | 374 | return $m[1] . $m[6]; 375 | } 376 | 377 | /** 378 | * Get registered shortcodes 379 | * 380 | * @return array shortcode tags. 381 | */ 382 | public function getRegistered() 383 | { 384 | return $this->registered; 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /src/Facades/Shortcode.php: -------------------------------------------------------------------------------- 1 | compiler = $compiler; 22 | } 23 | 24 | /** 25 | * Register a new shortcode 26 | * 27 | * @param string $name 28 | * @param callable|string $callback 29 | * 30 | * @return \Webwizo\Shortcodes\Shortcode 31 | */ 32 | public function register($name, $callback) 33 | { 34 | $this->compiler->add($name, $callback); 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * Enable the laravel-shortcodes 41 | * 42 | * @return \Webwizo\Shortcodes\Shortcode 43 | */ 44 | public function enable() 45 | { 46 | $this->compiler->enable(); 47 | 48 | return $this; 49 | } 50 | 51 | /** 52 | * Disable the laravel-shortcodes 53 | * 54 | * @return \Webwizo\Shortcodes\Shortcode 55 | */ 56 | public function disable() 57 | { 58 | $this->compiler->disable(); 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Compile the given string 65 | * 66 | * @param string $value 67 | * 68 | * @return string 69 | */ 70 | public function compile($value) 71 | { 72 | // Always enable when we call the compile method directly 73 | $this->enable(); 74 | 75 | // return compiled contents 76 | return $this->compiler->compile($value); 77 | } 78 | 79 | /** 80 | * Remove all shortcode tags from the given content. 81 | * 82 | * @param string $value 83 | * 84 | * @return string 85 | */ 86 | public function strip($value) 87 | { 88 | return $this->compiler->strip($value); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/ShortcodesServiceProvider.php: -------------------------------------------------------------------------------- 1 | enableCompiler(); 19 | } 20 | 21 | /** 22 | * Enable the compiler. 23 | */ 24 | public function enableCompiler() 25 | { 26 | // Check if the compiler is auto enabled 27 | $state = $this->app['config']->get('laravel-shortcodes::enabled', false); 28 | 29 | // Enable when needed 30 | if ($state) { 31 | $this->app['shortcode.compiler']->enable(); 32 | } 33 | } 34 | 35 | /** 36 | * Register the service provider. 37 | * 38 | * @return void 39 | */ 40 | public function register() 41 | { 42 | $this->registerShortcodeCompiler(); 43 | $this->registerShortcode(); 44 | $this->registerView(); 45 | } 46 | 47 | /** 48 | * Register short code compiler. 49 | */ 50 | public function registerShortcodeCompiler() 51 | { 52 | $this->app->singleton('shortcode.compiler', function ($app) { 53 | return new ShortcodeCompiler(); 54 | }); 55 | } 56 | 57 | /** 58 | * Register the shortcode. 59 | */ 60 | public function registerShortcode() 61 | { 62 | $this->app->singleton('shortcode', function ($app) { 63 | return new Shortcode($app['shortcode.compiler']); 64 | }); 65 | } 66 | 67 | /** 68 | * Register Laravel view. 69 | */ 70 | public function registerView() 71 | { 72 | $finder = $this->app['view']->getFinder(); 73 | 74 | $this->app->singleton('view', function ($app) use ($finder) { 75 | // Next we need to grab the engine resolver instance that will be used by the 76 | // environment. The resolver will be used by an environment to get each of 77 | // the various engine implementations such as plain PHP or Blade engine. 78 | $resolver = $app['view.engine.resolver']; 79 | $env = new Factory($resolver, $finder, $app['events'], $app['shortcode.compiler']); 80 | 81 | // We will also set the container instance on this view environment since the 82 | // view composers may be classes registered in the container, which allows 83 | // for great testable, flexible composers for the application developer. 84 | $env->setContainer($app); 85 | $env->share('app', $app); 86 | 87 | return $env; 88 | }); 89 | } 90 | 91 | /** 92 | * Get the services provided by the provider. 93 | * 94 | * @return array 95 | */ 96 | public function provides() 97 | { 98 | return [ 99 | 'shortcode', 100 | 'shortcode.compiler', 101 | 'view' 102 | ]; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/View/Factory.php: -------------------------------------------------------------------------------- 1 | shortcode = $shortcode; 32 | } 33 | 34 | /** 35 | * Get the evaluated view contents for the given view. 36 | * 37 | * @param string $view 38 | * @param array $data 39 | * @param array $mergeData 40 | * 41 | * @return \Illuminate\Contracts\View\View|string|View 42 | */ 43 | public function make($view, $data = [], $mergeData = []) 44 | { 45 | $path = $this->finder->find( 46 | $view = $this->normalizeName($view) 47 | ); 48 | 49 | // Next, we will create the view instance and call the view creator for the view 50 | // which can set any data, etc. Then we will return the view instance back to 51 | // the caller for rendering or performing other view manipulations on this. 52 | $data = array_merge($mergeData, $this->parseData($data)); 53 | 54 | return tap(new View($this->shortcode, $this, $this->getEngineFromPath($path), $view, $path, $data), function ($view) { 55 | $this->callCreator($view); 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/View/View.php: -------------------------------------------------------------------------------- 1 | shortcode = $shortcode; 35 | } 36 | 37 | /** 38 | * Enable the shortcodes 39 | */ 40 | public function withShortcodes() 41 | { 42 | $this->shortcode->enable(); 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Disable the shortcodes 49 | */ 50 | public function withoutShortcodes() 51 | { 52 | $this->shortcode->disable(); 53 | 54 | return $this; 55 | } 56 | 57 | public function withStripShortcodes() 58 | { 59 | $this->shortcode->setStrip(true); 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * Get the contents of the view instance. 66 | * 67 | * @return string 68 | */ 69 | protected function renderContents() 70 | { 71 | $this->shortcode->viewData($this->getData()); 72 | // We will keep track of the amount of views being rendered so we can flush 73 | // the section after the complete rendering operation is done. This will 74 | // clear out the sections for any separate views that may be rendered. 75 | $this->factory->incrementRender(); 76 | $this->factory->callComposer($this); 77 | $contents = $this->getContents(); 78 | if ($this->shortcode->getStrip()) { 79 | // strip content without shortcodes 80 | $contents = $this->shortcode->strip($contents); 81 | } else { 82 | // compile the shortcodes 83 | $contents = $this->shortcode->compile($contents); 84 | } 85 | // Once we've finished rendering the view, we'll decrement the render count 86 | // so that each sections get flushed out next time a view is created and 87 | // no old sections are staying around in the memory of an environment. 88 | $this->factory->decrementRender(); 89 | 90 | return $contents; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/NamespaceTest.php: -------------------------------------------------------------------------------- 1 | getFinder(); 12 | 13 | app()->register('Webwizo\Shortcodes\ShortcodesServiceProvider'); 14 | 15 | $freshFactory = app('view')->getFinder(); 16 | 17 | $this->assertEquals($factory, $freshFactory); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/ShortcodeTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(\Webwizo\Shortcodes\Shortcode::class, $this->shortcode); 12 | } 13 | 14 | public function testRegistrationAndCompileShortcode() 15 | { 16 | $this->shortcode->register('b', function ($shortcode, $content) { 17 | return sprintf('%s', $shortcode->class, $content); 18 | }); 19 | 20 | $compiled = $this->shortcode->compile('[b class="bold"]Bold Text[/b]'); 21 | 22 | $this->assertEquals('Bold Text', $compiled); 23 | } 24 | 25 | public function testFactoryViewMake() 26 | { 27 | $factory = app('view'); 28 | 29 | $factory->addNamespace('Test', __DIR__ . '/views'); 30 | 31 | $this->assertTrue($factory->make('Test::test') instanceof View); 32 | } 33 | 34 | public function testStripShortcode() 35 | { 36 | $this->shortcode->register('shortcode', function ($shortcode, $content) { 37 | return 'foobar'; 38 | }); 39 | 40 | $compiled = $this->shortcode->strip('[shortcode]Text[/shortcode]'); 41 | 42 | $this->assertEmpty($compiled); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | shortcode = app()->make('shortcode'); 17 | } 18 | 19 | protected function getPackageProviders($app) 20 | { 21 | return ['Webwizo\Shortcodes\ShortcodesServiceProvider']; 22 | } 23 | 24 | protected function getPackageAliases($app) 25 | { 26 | return [ 27 | 'Shortcode' => 'Webwizo\Shortcodes\Facades\Shortcode' 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/views/test.blade.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webwizo/laravel-shortcodes/6c1b7dff8c97e7e321a272898cff63ae46fbf99c/tests/views/test.blade.php --------------------------------------------------------------------------------