├── .gitignore ├── .travis.yml ├── LICENSE ├── composer.json ├── readme.md ├── spec └── Laracasts │ └── Utilities │ └── JavaScript │ └── PHPToJavaScriptTransformerSpec.php └── src ├── Laracasts └── Utilities │ ├── JavaScript │ ├── Facades │ │ └── JavaScript.php │ ├── LaravelViewBinder.php │ ├── PHPToJavaScriptTransformer.php │ └── ViewBinder.php │ └── UtilitiesServiceProvider.php └── config └── config.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.4 5 | - 5.5 6 | - 5.6 7 | - hhvm 8 | 9 | before_script: 10 | - composer install --dev --prefer-source --no-interaction 11 | 12 | script: vendor/bin/phpspec run 13 | -------------------------------------------------------------------------------- /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": ["laravel", "javascript"], 5 | "authors": [ 6 | { 7 | "name": "Jeffrey Way", 8 | "email": "jeffrey@laracasts.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.4.0", 13 | "illuminate/support": "4.1.*" 14 | }, 15 | "require-dev": { 16 | "phpspec/phpspec": "2.0.*@dev" 17 | }, 18 | "autoload": { 19 | "psr-0": { 20 | "Laracasts\\Utilities": "src/" 21 | } 22 | }, 23 | "minimum-stability": "stable" 24 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Transform PHP Vars to JavaScript 2 | 3 | Often, you'll find yourself in situations, where you want to pass some server-side string/array/collection/whatever 4 | to your JavaScript. Traditionally, this can be a bit of a pain - especially as your app grows. 5 | 6 | This package simplifies the process drastically. 7 | 8 | ## Installation 9 | 10 | Begin by installing this package through Composer. 11 | 12 | ```js 13 | { 14 | "require": { 15 | "laracasts/utilities": "1.0" 16 | } 17 | } 18 | ``` 19 | 20 | ### Laravel Users 21 | 22 | If you are a Laravel user, then there is a service provider that you can make use of to automatically prepare the bindings and such. 23 | 24 | ```php 25 | 26 | // app/config/app.php 27 | 28 | 'providers' => [ 29 | '...', 30 | 'Laracasts\Utilities\UtilitiesServiceProvider' 31 | ]; 32 | ``` 33 | 34 | When this provider is booted, you'll have access to a helpful `JavaScript` facade, which you may use in your controllers. 35 | 36 | ```php 37 | public function index() 38 | { 39 | JavaScript::put([ 40 | 'foo' => 'bar', 41 | 'user' => User::first(), 42 | 'age' => 29 43 | ]); 44 | 45 | return View::make('hello'); 46 | } 47 | ``` 48 | 49 | Using the code above, you'll now be able to access `foo`, `user`, and `age` from your JavaScript. 50 | 51 | ```js 52 | console.log(foo); // bar 53 | console.log(user); // User Obj 54 | console.log(age); // 29 55 | ``` 56 | 57 | ### Defaults 58 | 59 | If using Laravel, there are only two configuration options that you'll need to worry about. First, publish the default configuration. 60 | 61 | ```bash 62 | php artisan config:publish laracasts/utilities 63 | ``` 64 | 65 | This will add a new configuration file to: `app/config/packages/laracasts/utilities`. 66 | 67 | ```php 68 | 69 | 'hello', 83 | 84 | /* 85 | |-------------------------------------------------------------------------- 86 | | JavaScript Namespace 87 | |-------------------------------------------------------------------------- 88 | | 89 | | By default, we'll add variables to the global window object. 90 | | It's recommended that you change this to some namespace - anything. 91 | | That way, from your JS, you may do something like `Laracasts.myVar`. 92 | | 93 | */ 94 | 'js_namespace' => 'window' 95 | 96 | ]; 97 | ``` 98 | 99 | #### bind_js_vars_to_this_view 100 | 101 | You need to update this file to specify which view you want the new transformed JavaScript variables to be prepended to. Typically, your footer is a good place for this. 102 | 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. 103 | 104 | #### js_namespace 105 | 106 | By default, all JavaScript vars will be nested under the global `window` object. You'll likely want to change this. Update the 107 | `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), 108 | then you'll access all variables, like so: 109 | 110 | ```js 111 | MyNewNamespace.varName 112 | ``` 113 | 114 | ### Without Laravel 115 | 116 | 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.) 117 | 118 | 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. 119 | 120 | ```php 121 | put(['foo' => 'bar']); 140 | ``` 141 | 142 | Now, you can access `window.foo` from your JavaScript. 143 | 144 | Remember, though, this is only necessary if you aren't using Laravel. If you are, then just reference the service provider, as demonstrated above. 145 | 146 | ## License 147 | 148 | [View the license](https://github.com/laracasts/PHP-Vars-To-Js-Transformer/blob/master/LICENSE) for this repo. 149 | -------------------------------------------------------------------------------- /spec/Laracasts/Utilities/JavaScript/PHPToJavaScriptTransformerSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($viewBinder); 15 | } 16 | 17 | function it_is_initializable() 18 | { 19 | $this->shouldHaveType('Laracasts\Utilities\JavaScript\PHPToJavaScriptTransformer'); 20 | } 21 | 22 | function it_nests_all_vars_under_namespace() 23 | { 24 | // defaulting to window 25 | $this->buildJavaScriptSyntax([]) 26 | ->shouldMatch('/window.window = window.window || {};/'); 27 | } 28 | 29 | function it_transforms_php_strings() 30 | { 31 | $this->buildJavaScriptSyntax(['foo' => 'bar']) 32 | ->shouldMatch("/window.foo = 'bar';/"); 33 | } 34 | 35 | function it_transforms_php_arrays() 36 | { 37 | $this->buildJavaScriptSyntax(['letters' => ['a', 'b']]) 38 | ->shouldMatch('/window.letters = \["a","b"\];/'); 39 | } 40 | 41 | function it_transforms_php_booleans() 42 | { 43 | $this->buildJavaScriptSyntax(['isFoo' => false]) 44 | ->shouldMatch('/window.isFoo = false;/'); 45 | } 46 | 47 | function it_transforms_numerics() 48 | { 49 | $this->buildJavaScriptSyntax(['age' => 10, 'sum' => 10.12, 'dec' => 0.5]) 50 | ->shouldMatch('/window.age = 10;window.sum = 10.12;window.dec = 0.5;/'); 51 | } 52 | 53 | function it_throws_an_exception_if_an_object_cant_be_transformed(\StdClass $obj) 54 | { 55 | $this->shouldThrow('Exception') 56 | ->duringBuildJavaScriptSyntax(['foo' => $obj]); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Laracasts/Utilities/JavaScript/Facades/JavaScript.php: -------------------------------------------------------------------------------- 1 | event = $event; 24 | $this->viewToBindVariables = $viewToBindVariables; 25 | } 26 | 27 | /** 28 | * Bind the given JavaScript to the 29 | * view using Laravel event listeners 30 | * 31 | * @param $js The ready-to-go JS 32 | */ 33 | public function bind($js) 34 | { 35 | $this->event->listen("composing: {$this->viewToBindVariables}", function() use ($js) 36 | { 37 | echo ""; 38 | }); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/Laracasts/Utilities/JavaScript/PHPToJavaScriptTransformer.php: -------------------------------------------------------------------------------- 1 | viewBinder = $viewBinder; 35 | $this->namespace = $namespace; 36 | } 37 | 38 | /** 39 | * Bind given array of variables to view 40 | * 41 | * @param array $vars 42 | */ 43 | public function put(array $vars) 44 | { 45 | // First, we have to translate the 46 | // variables to something JS-friendly. 47 | $js = $this->buildJavaScriptSyntax($vars); 48 | 49 | // This is what handles the process of binding 50 | // our JS vars to the view/page. 51 | $this->viewBinder->bind($js); 52 | } 53 | 54 | /** 55 | * Translate the array of PHP vars 56 | * to JavaScript syntax. 57 | * 58 | * @param array $vars 59 | * @internal param $js 60 | * 61 | * @return array 62 | */ 63 | public function buildJavaScriptSyntax(array $vars) 64 | { 65 | $js = $this->buildNamespaceDeclaration(); 66 | 67 | foreach ($vars as $key => $value) { 68 | $js .= $this->buildVariableInitialization($key, $value); 69 | } 70 | 71 | return $js; 72 | } 73 | 74 | /** 75 | * Create the namespace that all 76 | * vars will be nested under. 77 | * 78 | * @return string 79 | */ 80 | protected function buildNamespaceDeclaration() 81 | { 82 | return "window.{$this->namespace} = window.{$this->namespace} || {};"; 83 | } 84 | 85 | /** 86 | * Translate a single PHP var to JS 87 | * 88 | * @param $key 89 | * @param $value 90 | * 91 | * @return string 92 | */ 93 | protected function buildVariableInitialization($key, $value) 94 | { 95 | return "{$this->namespace}.{$key} = {$this->optimizeValueForJavaScript($value)};"; 96 | } 97 | 98 | /** 99 | * Format a value for JavaScript 100 | * 101 | * @param $value 102 | * 103 | * @throws \Exception 104 | * @return bool|float|string 105 | */ 106 | protected function optimizeValueForJavaScript($value) 107 | { 108 | // For every kind of type, let's see 109 | // if it needs to be transformed for JS 110 | foreach ($this->types as $transformer) 111 | { 112 | $js = $this->{"transform{$transformer}"}($value); 113 | 114 | if ($js) return $js; 115 | } 116 | } 117 | 118 | /** 119 | * @param $value 120 | * @return string 121 | */ 122 | protected function transformString($value) 123 | { 124 | if (is_string($value)) 125 | { 126 | return "'{$this->escape($value)}'"; 127 | } 128 | } 129 | 130 | /** 131 | * @param $value 132 | * @return string 133 | */ 134 | protected function transformArray($value) 135 | { 136 | if (is_array($value)) 137 | { 138 | return json_encode($value); 139 | } 140 | } 141 | 142 | /** 143 | * @param $value 144 | * @return mixed 145 | */ 146 | protected function transformNumeric($value) 147 | { 148 | if (is_numeric($value)) 149 | { 150 | return $value; 151 | } 152 | } 153 | 154 | /** 155 | * @param $value 156 | * @return string 157 | */ 158 | protected function transformBoolean($value) 159 | { 160 | if (is_bool($value)) 161 | { 162 | return $value ? 'true' : 'false'; 163 | } 164 | } 165 | 166 | /** 167 | * @param $value 168 | * @return string 169 | * @throws \Exception 170 | */ 171 | protected function transformObject($value) 172 | { 173 | if (is_object($value)) 174 | { 175 | // If a toJson() method exists, we'll assume that 176 | // the object can cast itself automatically 177 | if (method_exists($value, 'toJson')) return $value; 178 | 179 | // Otherwise, if the object doesn't even have 180 | // a toString method, we can't proceed. 181 | if ( ! method_exists($value, '__toString')) 182 | { 183 | throw new Exception('The provided object needs a __toString() method.'); 184 | } 185 | 186 | return "'{$value}'"; 187 | } 188 | } 189 | 190 | /** 191 | * Escape single quotes (for now). 192 | * What else do we need to worry about? 193 | * 194 | * @param $value 195 | * 196 | * @return mixed 197 | */ 198 | protected function escape($value) 199 | { 200 | return str_replace("'", "\\'", $value); 201 | } 202 | 203 | } -------------------------------------------------------------------------------- /src/Laracasts/Utilities/JavaScript/ViewBinder.php: -------------------------------------------------------------------------------- 1 | app->bind('JavaScript', function($app) { 26 | $view = Config::get('utilities::config.bind_js_vars_to_this_view'); 27 | $namespace = Config::get('utilities::config.js_namespace'); 28 | 29 | $binder = new LaravelViewBinder($app['events'], $view); 30 | 31 | return new PHPToJavaScriptTransformer($binder, $namespace); 32 | }); 33 | } 34 | 35 | public function boot() 36 | { 37 | $this->package('laracasts/utilities'); 38 | 39 | // JavaScript::javaScript(['foo' => 'bar']) 40 | AliasLoader::getInstance()->alias( 41 | 'JavaScript', 42 | 'Laracasts\Utilities\JavaScript\Facades\JavaScript' 43 | ); 44 | } 45 | 46 | 47 | /** 48 | * The service provided 49 | * 50 | * @return array 51 | */ 52 | public function provides() 53 | { 54 | return ['JavaScript']; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | 'hello', 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | JavaScript Namespace 19 | |-------------------------------------------------------------------------- 20 | | 21 | | By default, we'll add variables to the global window object. 22 | | It's recommended that you change this to some namespace - anything. 23 | | That way, from your JS, you may do something like `Laracasts.myVar`. 24 | | 25 | */ 26 | 'js_namespace' => 'window' 27 | 28 | ]; --------------------------------------------------------------------------------