├── .gitignore ├── .travis.yml ├── LICENSE ├── composer.json ├── readme.md ├── spec └── Laracasts │ └── Utilities │ └── JavaScript │ └── Transformers │ └── TransformerSpec.php └── src ├── JavaScriptFacade.php ├── JavaScriptServiceProvider.php ├── LaravelViewBinder.php ├── Transformers ├── DefaultTransformer.php ├── ObjectTransformer.php └── Transformer.php ├── ViewBinder.php ├── config └── javascript.php └── helpers.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.5 5 | - 5.6 6 | - 7.0 7 | 8 | before_script: 9 | - composer install --dev --prefer-source --no-interaction 10 | 11 | script: vendor/bin/phpspec run 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jeffrey Way 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. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laracasts/utilities", 3 | "description": "Transform your PHP to JavaScript", 4 | "keywords": [ 5 | "laravel", 6 | "javascript" 7 | ], 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Jeffrey Way", 12 | "email": "jeffrey@laracasts.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.5.0|>=7.2.5|>=8.0.0", 17 | "illuminate/support": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" 18 | }, 19 | "require-dev": { 20 | "phpspec/phpspec": ">=2.0" 21 | }, 22 | "autoload": { 23 | "files": [ 24 | "src/helpers.php" 25 | ], 26 | "psr-4": { 27 | "Laracasts\\Utilities\\JavaScript\\": "src/" 28 | } 29 | }, 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "Laracasts\\Utilities\\JavaScript\\JavaScriptServiceProvider" 34 | ], 35 | "aliases": { 36 | "JavaScript": "Laracasts\\Utilities\\JavaScript\\JavaScriptFacade" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Transform PHP Vars to JavaScript 2 | 3 | [![Build Status](https://travis-ci.org/laracasts/PHP-Vars-To-Js-Transformer.png?branch=master)](https://travis-ci.org/laracasts/PHP-Vars-To-Js-Transformer) 4 | 5 | Often, you'll find yourself in situations, where you want to pass some server-side string/array/collection/whatever 6 | to your JavaScript. Traditionally, this can be a bit of a pain - especially as your app grows. 7 | 8 | This package simplifies the process drastically. 9 | 10 | ## Installation 11 | 12 | Begin by installing this package through Composer. 13 | 14 | ```bash 15 | composer require laracasts/utilities 16 | ``` 17 | 18 | > If you use Laravel 4: instead install `~1.0` of this package (and use the documentation for that release). For Laravel 5 (or non-Laravel), `~2.0` will do the trick! 19 | 20 | ### Laravel Users 21 | 22 | For Laravel users, there is a service provider you can make use of to automatically register the necessary bindings. 23 | 24 | > Laravel 5.5+ users: this step may be skipped, as we can auto-register the package with the framework. 25 | 26 | ```php 27 | 28 | // config/app.php 29 | 30 | 'providers' => [ 31 | '...', 32 | 'Laracasts\Utilities\JavaScript\JavaScriptServiceProvider' 33 | ]; 34 | ``` 35 | 36 | 37 | When this provider is booted, you'll gain access to a helpful `JavaScript` facade, which you may use in your controllers. 38 | 39 | ```php 40 | public function index() 41 | { 42 | JavaScript::put([ 43 | 'foo' => 'bar', 44 | 'user' => User::first(), 45 | 'age' => 29 46 | ]); 47 | 48 | return View::make('hello'); 49 | } 50 | ``` 51 | 52 | > In Laravel 5, of course add `use JavaScript;` to the top of your controller. 53 | 54 | Using the code above, you'll now be able to access `foo`, `user`, and `age` from your JavaScript. 55 | 56 | ```js 57 | console.log(foo); // bar 58 | console.log(user); // User Obj 59 | console.log(age); // 29 60 | ``` 61 | 62 | This package, by default, binds your JavaScript variables to a "footer" view, which you will include. For example: 63 | 64 | ``` 65 | 66 |

My Page

67 | 68 | @include ('footer') // <-- Variables prepended to this view 69 | 70 | ``` 71 | 72 | Naturally, you can change this default to a different view. See below. 73 | 74 | ### Defaults 75 | 76 | If using Laravel, there are only two configuration options that you'll need to worry about. First, publish the default configuration. 77 | 78 | ```bash 79 | php artisan vendor:publish 80 | 81 | // Or... 82 | 83 | php artisan vendor:publish --provider="Laracasts\Utilities\JavaScript\JavaScriptServiceProvider" 84 | ``` 85 | 86 | This will add a new configuration file to: `config/javascript.php`. 87 | 88 | ```php 89 | 'footer', 103 | 104 | /* 105 | |-------------------------------------------------------------------------- 106 | | JavaScript Namespace 107 | |-------------------------------------------------------------------------- 108 | | 109 | | By default, we'll add variables to the global window object. However, 110 | | it's recommended that you change this to some namespace - anything. 111 | | That way, you can access vars, like "SomeNamespace.someVariable." 112 | | 113 | */ 114 | 'js_namespace' => 'window' 115 | 116 | ]; 117 | ``` 118 | 119 | #### bind_js_vars_to_this_view 120 | 121 | You need to update this file to specify which view you want your new JavaScript variables to be prepended to. Typically, your footer is a good place for this. 122 | 123 | If you include something like a `layouts/partials/footer` partial, where you store your footer and script references, then make the `bind_js_vars_to_this_view` key equal to that path. Behind the scenes, the Laravel implementation of this package will listen for when that view is composed, and essentially paste the JS variables within it. 124 | 125 | #### js_namespace 126 | 127 | By default, all JavaScript vars will be nested under the global `window` object. You'll likely want to change this. Update the 128 | `js_namespace` key with the name of your desired JavaScript namespace. It can be anything. Just remember: if you change this setting (which you should), 129 | then you'll access all JavaScript variables, like so: 130 | 131 | ```js 132 | MyNewNamespace.varName 133 | ``` 134 | 135 | #### Note 136 | Run this artisan command after changing the view path. 137 | ``` 138 | php artisan config:clear 139 | ``` 140 | 141 | ### Symfony2 142 | To use this component in Symfony2 applications you can try [this bundle](https://github.com/holyspecter/HospectPhpVarsToJsBundle), built on top of PHP-Vars-To-Js-Transformer. 143 | 144 | ### Without Laravel 145 | 146 | If you're not using Laravel, then you'll need to hard-wire things yourself. (Or, feel free to submit a pull request with an implementation for your desired framework.) 147 | 148 | First, create an implementation of the `Laracasts\Utilities\JavaScript\ViewBinder` interface. This class is in charge of inserting the given JavaScript into your view/page. 149 | 150 | ```php 151 | put(['foo' => 'bar']); 175 | ``` 176 | 177 | Now, you can access `window.foo` from your JavaScript. 178 | 179 | Remember, though, this is only necessary if you aren't using Laravel. If you are, then just reference the service provider, as demonstrated above. 180 | 181 | ## License 182 | 183 | [View the license](https://github.com/laracasts/PHP-Vars-To-Js-Transformer/blob/master/LICENSE) for this repo. 184 | -------------------------------------------------------------------------------- /spec/Laracasts/Utilities/JavaScript/Transformers/TransformerSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($viewBinder); 14 | } 15 | 16 | public function it_is_initializable() 17 | { 18 | $this->shouldHaveType('Laracasts\Utilities\JavaScript\Transformers\Transformer'); 19 | } 20 | 21 | public function it_binds_to_the_view(ViewBinder $viewBinder) 22 | { 23 | $viewBinder->bind('window.foo = "bar";')->shouldBeCalled(); 24 | 25 | $this->put(['foo' => 'bar'])->shouldReturn('window.foo = "bar";'); 26 | } 27 | 28 | public function it_uses_the_window_as_the_root_namespace_by_default() 29 | { 30 | $this->constructJavaScript([]) 31 | ->shouldEqual(''); 32 | } 33 | 34 | public function if_another_namespace_is_provided_it_will_use_that_instead(ViewBinder $viewBinder) 35 | { 36 | $this->beConstructedWith($viewBinder, 'Namespace'); 37 | 38 | $this->constructJavaScript([]) 39 | ->shouldEqual('window.Namespace = window.Namespace || {};'); 40 | } 41 | 42 | public function it_translates_an_array_of_key_value_pairs_to_javascript() 43 | { 44 | $this->put(['foo' => 'bar']) 45 | ->shouldMatch("/window.foo = \"bar\";/"); 46 | } 47 | 48 | public function it_translates_two_arguments_as_key_and_value_to_javascript() 49 | { 50 | $this->put('foo', 'bar') 51 | ->shouldMatch("/window.foo = \"bar\";/"); 52 | } 53 | 54 | public function it_takes_exception_if_incorrect_arguments_are_passed_to_put() 55 | { 56 | $this->shouldThrow('Exception')->duringPut(); 57 | } 58 | 59 | public function it_transforms_php_strings() 60 | { 61 | $this->constructJavaScript(['foo' => 'bar']) 62 | ->shouldMatch("/window.foo = \"bar\";/"); 63 | } 64 | 65 | public function it_transforms_multiline_php_strings() 66 | { 67 | $this->constructJavaScript(['foo' => "new\nline"]) 68 | ->shouldContain('\n'); // String 69 | $this->constructJavaScript(['foo' => "new\nline"]) 70 | ->shouldNotContain("\n"); // Newline character 71 | } 72 | 73 | public function it_transforms_php_arrays() 74 | { 75 | $this->constructJavaScript(['letters' => ['a', 'b']]) 76 | ->shouldMatch('/window.letters = \["a","b"\];/'); 77 | } 78 | 79 | public function it_transforms_multiline_strings_in_php_arrays() 80 | { 81 | $this->constructJavaScript(['newline' => ["new\nline"]]) 82 | ->shouldContain('\n'); // String 83 | $this->constructJavaScript(['newline' => ["new\nline"]]) 84 | ->shouldNotContain("\n"); // Newline character 85 | } 86 | 87 | public function it_transforms_php_booleans() 88 | { 89 | $this->constructJavaScript(['isFoo' => false]) 90 | ->shouldMatch('/window.isFoo = false;/'); 91 | } 92 | 93 | public function it_transforms_numerics() 94 | { 95 | $this->constructJavaScript(['age' => 10, 'sum' => 10.12, 'dec' => 0]) 96 | ->shouldMatch('/window.age = 10;window.sum = 10.12;window.dec = 0;/'); 97 | } 98 | 99 | public function it_transforms_null_values() 100 | { 101 | $this->constructJavaScript(['age' => null, 'sum' => null]) 102 | ->shouldMatch('/window.age = null;window.sum = null;/'); 103 | } 104 | 105 | public function it_transforms_json_serializable_objects() 106 | { 107 | $this->constructJavaScript(['foo' => new JsonSerializableClass]) 108 | ->shouldMatch('/window.foo = {"key":"value"}/'); 109 | } 110 | 111 | public function it_throws_an_exception_if_an_object_cant_be_transformed(Transformer $obj) 112 | { 113 | $this->shouldThrow('Exception') 114 | ->duringConstructJavaScript(['foo' => $obj]); 115 | } 116 | 117 | public function it_does_not_throw_an_exception_for_stdClass(\StdClass $obj) 118 | { 119 | $this->constructJavaScript(['foo' => $obj]) 120 | ->shouldMatch('/window.window = window.window || {};/'); 121 | } 122 | } 123 | 124 | class JsonSerializableClass implements \JsonSerializable 125 | { 126 | public function jsonSerialize() 127 | { 128 | return ['key' => 'value']; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/JavaScriptFacade.php: -------------------------------------------------------------------------------- 1 | app->singleton('JavaScript', function ($app) { 19 | return new Transformer( 20 | new LaravelViewBinder($app['events'], config('javascript.bind_js_vars_to_this_view')), 21 | config('javascript.js_namespace') 22 | ); 23 | }); 24 | 25 | $this->mergeConfigFrom( 26 | __DIR__ . '/config/javascript.php', 27 | 'javascript' 28 | ); 29 | } 30 | 31 | /** 32 | * Publish the plugin configuration. 33 | */ 34 | public function boot() 35 | { 36 | $this->publishes([ 37 | __DIR__ . '/config/javascript.php' => config_path('javascript.php') 38 | ]); 39 | 40 | if (class_exists('Illuminate\Foundation\AliasLoader')) { 41 | AliasLoader::getInstance()->alias( 42 | 'JavaScript', 43 | 'Laracasts\Utilities\JavaScript\JavaScriptFacade' 44 | ); 45 | } else { 46 | class_alias('Laracasts\Utilities\JavaScript\JavaScriptFacade', 'JavaScript'); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/LaravelViewBinder.php: -------------------------------------------------------------------------------- 1 | event = $event; 32 | $this->views = str_replace('/', '.', (array)$views); 33 | } 34 | 35 | /** 36 | * Bind the given JavaScript to the view. 37 | * 38 | * @param string $js 39 | */ 40 | public function bind($js) 41 | { 42 | foreach ($this->views as $view) { 43 | $this->event->listen("composing: {$view}", function () use ($js) { 44 | echo ""; 45 | }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Transformers/DefaultTransformer.php: -------------------------------------------------------------------------------- 1 | viewBinder = $viewBinder; 33 | $this->namespace = $namespace; 34 | } 35 | 36 | /** 37 | * Bind the given array of variables to the view. 38 | */ 39 | public function put() 40 | { 41 | $js = $this->constructJavaScript($this->normalizeInput(func_get_args())); 42 | 43 | $this->viewBinder->bind($js); 44 | 45 | return $js; 46 | } 47 | 48 | /** 49 | * Translate the array of PHP variables to a JavaScript syntax. 50 | * 51 | * @param array $variables 52 | * @return array 53 | */ 54 | public function constructJavaScript($variables) 55 | { 56 | return $this->constructNamespace() . collect($variables)->map(function ($value, $name) { 57 | return $this->initializeVariable($name, $value); 58 | })->implode(''); 59 | } 60 | 61 | /** 62 | * Create the namespace to which all vars are nested. 63 | * 64 | * @return string 65 | */ 66 | protected function constructNamespace() 67 | { 68 | if ($this->namespace == 'window') { 69 | return ''; 70 | } 71 | 72 | return "window.{$this->namespace} = window.{$this->namespace} || {};"; 73 | } 74 | 75 | /** 76 | * Translate a single PHP var to JS. 77 | * 78 | * @param string $key 79 | * @param string $value 80 | * @return string 81 | */ 82 | protected function initializeVariable($key, $value) 83 | { 84 | return "{$this->namespace}.{$key} = {$this->convertToJavaScript($value)};"; 85 | } 86 | 87 | /** 88 | * Format a value for JavaScript. 89 | * 90 | * @param string $value 91 | * @throws Exception 92 | * @return string 93 | */ 94 | protected function convertToJavaScript($value) 95 | { 96 | $transformer = is_object($value) ? ObjectTransformer::class : DefaultTransformer::class; 97 | 98 | return (new $transformer)->transform($value); 99 | } 100 | 101 | /** 102 | * Normalize the input arguments. 103 | * 104 | * @param mixed $arguments 105 | * @return array 106 | * @throws \Exception 107 | */ 108 | protected function normalizeInput($arguments) 109 | { 110 | if (is_array($arguments[0])) { 111 | return $arguments[0]; 112 | } 113 | 114 | if (count($arguments) == 2) { 115 | return [$arguments[0] => $arguments[1]]; 116 | } 117 | 118 | throw new Exception('Try JavaScript::put(["foo" => "bar"])'); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/ViewBinder.php: -------------------------------------------------------------------------------- 1 | 'footer', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | JavaScript Namespace 21 | |-------------------------------------------------------------------------- 22 | | 23 | | By default, we'll add variables to the global window object. However, 24 | | it's recommended that you change this to some namespace - anything. 25 | | That way, you can access vars, like "SomeNamespace.someVariable." 26 | | 27 | */ 28 | 'js_namespace' => 'window' 29 | 30 | ]; 31 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | basePath(); 13 | $adjustedPath = $path ? "/$path" : $path; 14 | 15 | return "$basePath/config$adjustedPath"; 16 | } 17 | } 18 | --------------------------------------------------------------------------------