├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .styleci.yml ├── composer.json ├── config └── shortcodes.php ├── contributing.md ├── license.md ├── phpunit.xml ├── readme.md ├── src ├── Commands │ └── MakeShortcodeCommand.php ├── Debugbar │ └── ShortcodesCollector.php ├── Facades │ └── Shortcodes.php ├── LaravelShortcodesServiceProvider.php ├── Manager.php ├── Renderer.php ├── Shortcode.php ├── ShortcodeContract.php ├── Shortcodes │ └── BShortcode.php ├── View │ ├── Factory.php │ └── View.php └── helpers.php ├── stubs └── shortcode.stub ├── tests ├── Resources │ ├── BShortcode.php │ ├── CastsShortcode.php │ ├── ExceptionShortcode.php │ ├── ExceptionViewShortcode.php │ ├── HrShortcode.php │ └── ValidationShortcode.php ├── TestCase.php ├── Unit │ ├── BladeTest.php │ ├── HelpersTest.php │ ├── ManagerTest.php │ ├── ShortcodeTest.php │ └── ViewTest.php └── views │ ├── bold.blade.php │ ├── directive-block.blade.php │ ├── directive-inline.blade.php │ ├── exception.blade.php │ └── shortcode-exception.blade.php └── views └── b.blade.php /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push] 3 | jobs: 4 | run-tests: 5 | runs-on: ubuntu-latest 6 | name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} 7 | strategy: 8 | fail-fast: true 9 | matrix: 10 | php: [ 7.4, 8.0, 8.1 ] 11 | laravel: [ 6.*, 8.*, 9.* ] 12 | exclude: 13 | - php: 8.0 14 | laravel: 6.* 15 | - php: 8.1 16 | laravel: 6.* 17 | - php: 7.4 18 | laravel: 9.* 19 | include: 20 | - aravel: 6.* 21 | testbench: 4.* 22 | phpunit: 8.* 23 | - laravel: 8.* 24 | testbench: 6.* 25 | phpunit: 9.* 26 | - laravel: 9.* 27 | testbench: 7.* 28 | phpunit: 9.* 29 | steps: 30 | - name: Check out repository code 31 | uses: actions/checkout@v2 32 | - name: Setup PHP 33 | uses: shivammathur/setup-php@v2 34 | with: 35 | php-version: ${{ matrix.php }} 36 | - name: Install dependencies 37 | run: | 38 | composer require "illuminate/support:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "phpunit/phpunit:${{ matrix.phpunit }}" --no-interaction --no-update 39 | composer update --prefer-stable --prefer-dist --no-interaction --no-suggest 40 | - name: Cache dependencies 41 | uses: actions/cache@v1 42 | with: 43 | path: ~/.composer/cache/files 44 | key: dependencies-${{ matrix.dependency-version }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 45 | - name: Execute tests 46 | run: vendor/bin/phpunit 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /vendor 3 | npm-debug.log 4 | yarn-error.log 5 | composer.lock 6 | .phpunit.result.cache 7 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | finder: 4 | not-name: 5 | - "Renderer.php" 6 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vedmant/laravel-shortcodes", 3 | "description": "Wordress like shortcodes for Laravel", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Vedmant", 8 | "email": "vedmant@gmail.com", 9 | "homepage": "https://vedmant.com/laravel-shortcodes" 10 | } 11 | ], 12 | "homepage": "https://github.com/vedmant/laravel-shortcodes", 13 | "keywords": ["Laravel", "Shortcodes"], 14 | "require": { 15 | "php": ">=7.0.0", 16 | "illuminate/contracts": ">=5.5.0", 17 | "illuminate/view": ">=5.5.0", 18 | "illuminate/support": ">=5.5.0" 19 | }, 20 | "require-dev": { 21 | "phpunit/phpunit": "9.*", 22 | "mockery/mockery": "^1.2", 23 | "orchestra/testbench": "^4", 24 | "sempro/phpunit-pretty-print": "^1.0" 25 | }, 26 | "autoload": { 27 | "files": [ 28 | "src/helpers.php" 29 | ], 30 | "psr-4": { 31 | "Vedmant\\LaravelShortcodes\\": "src/" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Vedmant\\LaravelShortcodes\\Tests\\": "tests" 37 | } 38 | }, 39 | "extra": { 40 | "laravel": { 41 | "providers": [ 42 | "Vedmant\\LaravelShortcodes\\LaravelShortcodesServiceProvider" 43 | ], 44 | "aliases": { 45 | "FeedReader": "Vedmant\\LaravelShortcodes\\Facades\\FeedReader" 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /config/shortcodes.php: -------------------------------------------------------------------------------- 1 | true, 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Automatically render views 20 | |-------------------------------------------------------------------------- 21 | | 22 | | If it should automatically render shortcodes in views. 23 | | 24 | */ 25 | 26 | 'render_views' => true, 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Laravel Debugbar integration 31 | |-------------------------------------------------------------------------- 32 | | 33 | | If it should integrate in the Laravel Debug Bar. 34 | | 35 | */ 36 | 37 | 'debugbar' => true, 38 | 39 | ]; 40 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome and will be fully credited. 4 | 5 | Contributions are accepted via Pull Requests on [Github](https://github.com/vedmant/laravelshortcodes). 6 | 7 | ## Pull Requests 8 | 9 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 10 | 11 | - **Document any change in behaviour** - Make sure the `readme.md` and any other relevant documentation are kept up-to-date. 12 | 13 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 14 | 15 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 16 | 17 | - **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. 18 | 19 | 20 | **Happy coding**! 21 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 Vedmat, Inc. http://vedmant.com 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 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Laravel Shortcodes 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](license.md) 5 | [![Total Downloads][ico-downloads]][link-downloads] 6 | ![Tests](https://github.com/vedmant/laravel-shortcodes/actions/workflows/tests.yml/badge.svg) 7 | [![StyleCI](https://styleci.io/repos/182276041/shield)](https://styleci.io/repos/182276041) 8 | 9 | Wordpress based Shortcodes for [Laravel Framework](https://github.com/laravel/laravel) with shared variables, debugbar integration, 10 | flexible configurations and other useful features. 11 | 12 | Build powerful and simple layouts using shortcodes in the content or views like this: 13 | 14 | ```php 15 | [b]Bold text[/b] 16 | 17 | [row] 18 | [col md=8] 19 | [posts_list types="post,gallery" show_tags="yes"] 20 | [/col] 21 | [col md=4] 22 | [poll id="1"] 23 | [user_info username="test_user" website="mywebsite.com" active="yes"] 24 | [last_free_post title="Free Posts"] 25 | [/col] 26 | [/row] 27 | ``` 28 | 29 | ## Installation 30 | 31 | Via Composer 32 | 33 | ``` bash 34 | $ composer require vedmant/laravel-shortcodes 35 | ``` 36 | 37 | ## Configuraton 38 | 39 | Publish configuration. 40 | ```bash 41 | php artisan vendor:publish --tag=shortcodes 42 | ``` 43 | 44 | It will publish configuration file `shortcodes.php`, edit it as needed. 45 | 46 | 47 | ## Usage 48 | 49 | 50 | ### Shortcode class 51 | 52 | Shortcode class should extend abstract \Vedmant\LaravelShortcodes\Shortcode class. 53 | 54 | This packages adds the `make:shortcode` artisan command: 55 | ```bash 56 | php artisan make:shortcode PostsListShortcode 57 | ``` 58 | Which generates a shortcode class in the `app/Shortcodes` folder by default. 59 | 60 | 61 | ### Register shortcodes 62 | 63 | You can use `AppServiceProvider::boot` method to register all needed shortcodes. 64 | 65 | Using shortcode class: 66 | ```php 67 | Shortcodes::add('b', BShortcode::class); 68 | ``` 69 | 70 | Using shortcode classes in array, preferable for lots of shortcodes: 71 | ```php 72 | Shortcodes::add([ 73 | 'a' => AShortcode::class, 74 | 'b' => BShortcode::class, 75 | ]); 76 | ``` 77 | 78 | Using closure: 79 | ```php 80 | Shortcodes::add('test', function ($atts, $content, $tag, $manager) { 81 | return new HtmlString('some test shortcode'); 82 | }); 83 | ``` 84 | 85 | ### Render shortcodes 86 | 87 | #### Views auto-render 88 | 89 | By default this package extends the `View` class to parse all shortcodes during views rendering. 90 | This feature can be disabled in the config file: `'render_views' => false`. 91 | For better performance with lots of views it's advised to disable views auto-render. 92 | 93 | #### Enable / disable rendering per view 94 | 95 | Also to enable / disable rendering shortcodes for a specific view you can use: 96 | 97 | ```php 98 | view('some-view')->withShortcodes(); 99 | // Or 100 | view('some-view')->withoutShortcodes(); 101 | ``` 102 | 103 | #### Render shortcodes with the facade 104 | 105 | ```blade 106 | {{ Shortcodes::render('[b]bold[/b]') }} 107 | ``` 108 | 109 | #### Render shortcodes with blade directive 110 | 111 | ```blade 112 | @shortcodes 113 | [b class="block"]Content[/b] 114 | @endshortcodes 115 | 116 | Or 117 | 118 | @shortcodes('[b]bold[/b]') 119 | ``` 120 | 121 | #### Render shortcodes with `shortcodes()` helper 122 | 123 | ```blade 124 |
125 | {{ shortcodes('[b]bold[/b]') }} 126 |
127 | ``` 128 | 129 | ### Shared attributes 130 | 131 | Occasionally, you may need to share a piece of data with all shortcodes that are rendered by your application. 132 | You may do so using the shortode facade's `share` method. 133 | Typically, you should place calls to share in the controller, or within a service provider's boot method. 134 | ```php 135 | Shortcodes::share('post', $post); 136 | ``` 137 | 138 | Then you can get shared attributes in the shortcode class: 139 | 140 | ```php 141 | $post = $this->shared('post'); 142 | $allShared = $this->shared(); 143 | ``` 144 | 145 | ### Attribute casting 146 | 147 | The $casts property on your shortcode class provides a convenient method of converting attributes to 148 | common data types. The $casts property should be an array where the key is the name of the attribute 149 | being cast and the value is the type you wish to cast the column to. The supported cast types are: 150 | `int`, `integer`, `real`, `float`, `double`, `boolean`, `array` (comma separated values) and `date`. 151 | 152 | ```php 153 | class YourShortcode extends Shortcode 154 | { 155 | /** 156 | * The attributes that should be cast to native types. 157 | * 158 | * @var array 159 | */ 160 | protected $casts = [ 161 | 'show_ids' => 'array', 162 | ]; 163 | } 164 | ``` 165 | 166 | Now the `show_ids` attribute will always be cast to an array when you access it. 167 | (array attributes are casted from comma separated string, eg. "1,2,3"). 168 | 169 | 170 | ### Attribute validation 171 | 172 | There is a simple way to validate attributes. 173 | Error messages will be rendered on the shortcode place. 174 | For convenients it will return attributes. 175 | 176 | ```php 177 | class YourShortcode extends Shortcode 178 | { 179 | /** 180 | * Render shortcode 181 | * 182 | * @param string $content 183 | * @return string 184 | */ 185 | public function render($content) 186 | { 187 | $atts = $this->validate([ 188 | 'post_id' => 'required|numeric|exists:posts,id', 189 | ]); 190 | 191 | // 192 | } 193 | } 194 | ``` 195 | 196 | ### Option to not throw exceptions from shortcodes 197 | 198 | There is a useful option to aviod server (500) error for whole page when one of shortocode has thrown an exception. 199 | 200 | To enable it set `'throw_exceptions' => false,` in the `shortcodes.php` config file. 201 | 202 | This will render exception details in the place of a shortcode and will not crash whole page request with 500 error. 203 | It will still log exception to a log file and report to [Sentry](https://sentry.io/) if it's integrated. 204 | 205 | 206 | ### Generate data for documentation 207 | 208 | There can be hundreds of registered shortcodes and having a way to show documentation for all 209 | shortcodes is quite a good feature. There is simple method that will collect descriptions and attributes data 210 | from all registered shortcodes: 211 | ```php 212 | $data = Shortcodes::registeredData(); 213 | ``` 214 | It returns Collection object with generated data that can be used to generate any help information. 215 | 216 | 217 | ### Integration with Laravel Debugbar 218 | 219 | This packages supports [Laravel Debugbar](https://github.com/barryvdh/laravel-debugbar) 220 | and adds a tab with detailed info about rendered shortcodes. 221 | Integration can be disabled in the config file with option: `'debugbar' => false,`. 222 | 223 | 224 | ## Testing 225 | 226 | ``` bash 227 | $ vendor/bin/phpunit 228 | ``` 229 | 230 | 231 | ## TODO 232 | 233 | 1. Add custom widget for debugbar integration 234 | 1. Create performance profile tests, optimize performance 235 | 236 | ## Contributing 237 | 238 | Please see [contributing.md](contributing.md) for details and a todolist. 239 | 240 | ## Security 241 | 242 | If you discover any security related issues, please email vedmant@gmail.com instead of using the issue tracker. 243 | 244 | ## Credits 245 | 246 | - [All Contributors][link-contributors] 247 | 248 | ## License 249 | 250 | MIT. Please see the [license file](license.md) for more information. 251 | 252 | [ico-version]: https://img.shields.io/packagist/v/vedmant/laravel-shortcodes.svg?style=flat-square 253 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 254 | [ico-downloads]: https://img.shields.io/packagist/dt/vedmant/laravel-shortcodes.svg?style=flat-square 255 | [ico-travis]: https://img.shields.io/travis/vedmant/laravel-shortcodes/master.svg?style=flat-square 256 | 257 | [link-packagist]: https://packagist.org/packages/vedmant/laravel-shortcodes 258 | [link-downloads]: https://packagist.org/packages/vedmant/laravel-shortcodes 259 | [link-styleci]: https://github.styleci.io/repos/182276041 260 | [link-author]: https://github.com/vedmant 261 | [link-contributors]: ../../contributors 262 | -------------------------------------------------------------------------------- /src/Commands/MakeShortcodeCommand.php: -------------------------------------------------------------------------------- 1 | shortcodes[] = $data; 30 | } 31 | 32 | /** 33 | * Returns the list of shortcodes being profiled. 34 | * 35 | * @return array 36 | */ 37 | public function getShortcodes(): array 38 | { 39 | return $this->shortcodes; 40 | } 41 | 42 | /** 43 | * Called by the DebugBar when data needs to be collected. 44 | * 45 | * @return array Collected data 46 | */ 47 | public function collect() 48 | { 49 | $shortcodes = new Collection($this->shortcodes); 50 | 51 | $shortcodesData = $shortcodes->mapWithKeys(function ($data) { 52 | $time = $this->getDataFormatter()->formatDuration($data['time']); 53 | 54 | return [ 55 | "[{$data['tag']}] - {$time}" => $this->getVarDumper()->renderVar($data['shortcode']->atts()), 56 | ]; 57 | }); 58 | 59 | return [ 60 | 'count' => count($this->shortcodes), 61 | 'shortcodes' => $shortcodesData->prepend( 62 | $this->getDataFormatter()->formatDuration($shortcodes->sum('time')), 63 | 'Total time' 64 | ), 65 | ]; 66 | } 67 | 68 | /** 69 | * @return string 70 | */ 71 | public function getName() 72 | { 73 | return 'shortcodes'; 74 | } 75 | 76 | /** 77 | * @return array 78 | */ 79 | public function getWidgets() 80 | { 81 | return [ 82 | 'shortcodes' => [ 83 | 'icon' => 'tags', 84 | 'widget' => 'PhpDebugBar.Widgets.HtmlVariableListWidget', 85 | 'map' => 'shortcodes.shortcodes', 86 | 'default' => '[]', 87 | ], 88 | 'shortcodes:badge' => [ 89 | 'map' => 'shortcodes.count', 90 | 'default' => 'null', 91 | ], 92 | ]; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Facades/Shortcodes.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../views', 'shortcodes'); 21 | 22 | // Publishing is only necessary when using the CLI. 23 | if ($this->app->runningInConsole()) { 24 | $this->bootForConsole(); 25 | } 26 | 27 | if ($this->app['config']->get('shortcodes.debugbar') && $this->app->bound('debugbar')) { 28 | $this->app['debugbar']->addCollector(new ShortcodesCollector()); 29 | } 30 | 31 | Blade::directive('shortcodes', function ($expression) { 32 | if ($expression === '') { 33 | return ''; 34 | } else { 35 | return "render($expression); ?>"; 36 | } 37 | }); 38 | 39 | Blade::directive('endshortcodes', function () { 40 | return "render(ob_get_clean()); ?>"; 41 | }); 42 | } 43 | 44 | /** 45 | * Console-specific booting. 46 | * 47 | * @return void 48 | */ 49 | protected function bootForConsole() 50 | { 51 | // Publishing the configuration file. 52 | $this->publishes([ 53 | __DIR__.'/../config/shortcodes.php' => config_path('shortcodes.php'), 54 | ], 'shortcodes'); 55 | 56 | // Registering package commands. 57 | $this->commands([ 58 | MakeShortcodeCommand::class, 59 | ]); 60 | } 61 | 62 | /** 63 | * Register any package services. 64 | * 65 | * @return void 66 | */ 67 | public function register() 68 | { 69 | $this->mergeConfigFrom(__DIR__.'/../config/shortcodes.php', 'shortcodes'); 70 | 71 | // Register the service the package provides. 72 | $this->app->singleton('shortcodes', function ($app) { 73 | return new Manager($app, $app->make('config')->get('shortcodes')); 74 | }); 75 | 76 | $this->registerView(); 77 | } 78 | 79 | /** 80 | * Register the view environment. 81 | * 82 | * @return void 83 | */ 84 | public function registerView() 85 | { 86 | $this->app->singleton('view', function ($app) { 87 | // Next we need to grab the engine resolver instance that will be used by the 88 | // environment. The resolver will be used by an environment to get each of 89 | // the various engine implementations such as plain PHP or Blade engine. 90 | $resolver = $app['view.engine.resolver']; 91 | 92 | $finder = $app['view.finder']; 93 | 94 | $factory = new Factory($resolver, $finder, $app['events']); 95 | 96 | // We will also set the container instance on this view environment since the 97 | // view composers may be classes registered in the container, which allows 98 | // for great testable, flexible composers for the application developer. 99 | $factory->setContainer($app); 100 | 101 | $factory->share('app', $app); 102 | 103 | return $factory; 104 | }); 105 | } 106 | 107 | /** 108 | * Get the services provided by the provider. 109 | * 110 | * @return array 111 | */ 112 | public function provides() 113 | { 114 | return ['shortcodes', 'view']; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | app = $app; 44 | $this->config = $config; 45 | $this->renderer = new Renderer($app, $this); 46 | } 47 | 48 | /** 49 | * Share attribute. 50 | * 51 | * @param string $key 52 | * @param mixed $value 53 | * @return Manager 54 | */ 55 | public function share($key, $value) 56 | { 57 | $this->shared[$key] = $value; 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * Set / get shared variable. 64 | * 65 | * @param string $key 66 | * @param null $default 67 | * @return mixed 68 | */ 69 | public function shared($key = null, $default = null) 70 | { 71 | if ($key === null) { 72 | return $this->shared; 73 | } 74 | 75 | return Arr::get($this->shared, $key, $default); 76 | } 77 | 78 | /** 79 | * Register a shortcode. 80 | * 81 | * @param string|array $name 82 | * @param string|callable $callable 83 | * @return Manager 84 | */ 85 | public function add($name, $callable = null) 86 | { 87 | if (is_array($name)) { 88 | $this->renderer->shortcodes = array_merge($this->renderer->shortcodes, $name); 89 | } else { 90 | $this->renderer->shortcodes[$name] = $callable; 91 | } 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Unregister a shortcode. 98 | * 99 | * @param string $name 100 | * @return Manager 101 | */ 102 | public function remove($name) 103 | { 104 | unset($this->renderer->shortcodes[$name]); 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Get all registered shortcodes. 111 | * 112 | * @return array 113 | */ 114 | public function registered(): array 115 | { 116 | return $this->renderer->shortcodes; 117 | } 118 | 119 | /** 120 | * Get list of rendered shortcodes. 121 | * 122 | * @return array 123 | */ 124 | public function rendered(): array 125 | { 126 | return $this->renderer->rendered; 127 | } 128 | 129 | /** 130 | * Render shortcodes in the content. 131 | * 132 | * @param string $content 133 | * @return HtmlString 134 | */ 135 | public function render($content) 136 | { 137 | return new HtmlString($this->renderer->apply($content)); 138 | } 139 | 140 | /** 141 | * Generate all registered shortcodes info. 142 | * 143 | * @return Collection 144 | */ 145 | public function registeredData(): Collection 146 | { 147 | return (new Collection($this->renderer->shortcodes))->mapWithKeys(function ($class, $name) { 148 | if (! is_string($class) || ! class_exists($class)) { 149 | return []; 150 | } 151 | /** @var Shortcode $shortcode */ 152 | $shortcode = new $class($this->app, $this, [], $name); 153 | 154 | return [$name => [ 155 | 'class' => $class, 156 | 'name' => $name, 157 | 'description' => $shortcode->description, 158 | 'attributes' => $shortcode->attributes(), 159 | ]]; 160 | }); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/Renderer.php: -------------------------------------------------------------------------------- 1 | app = $app; 46 | $this->manager = $manager; 47 | } 48 | 49 | /** 50 | * Apply shortcodes to content. 51 | * 52 | * @param string $content 53 | * @return string 54 | */ 55 | public function apply($content) 56 | { 57 | return $this->doShortcode($content); 58 | } 59 | 60 | /** 61 | * Search content for shortcodes and filter shortcodes through their hooks. 62 | * 63 | * If there are no shortcode tags defined, then the content will be returned 64 | * without any filtering. This might cause issues when plugins are disabled but 65 | * the shortcode will still show up in the post or content. 66 | * 67 | * @param string $content Content to search for shortcodes. 68 | * @return string Content with shortcodes filtered out. 69 | */ 70 | private function doShortcode($content) 71 | { 72 | if (false === strpos($content, '[')) { 73 | return $content; 74 | } 75 | 76 | if (empty($this->shortcodes) || ! is_array($this->shortcodes)) { 77 | return $content; 78 | } 79 | 80 | // Find all registered tag names in $content. 81 | preg_match_all('@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches); 82 | $tagnames = array_intersect(array_keys($this->shortcodes), $matches[1]); 83 | 84 | if (empty($tagnames)) { 85 | return $content; 86 | } 87 | 88 | $pattern = $this->getShortcodeRegex($tagnames); 89 | $content = preg_replace_callback("/$pattern/", [$this, 'doShortcodeTag'], $content); 90 | 91 | // Always restore square braces so we don't break things like