├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── README.ru.md ├── composer.json ├── composer.lock ├── phpunit.xml.dist ├── src ├── JsonBehavior.php ├── JsonField.php └── JsonValidator.php └── tests ├── BehaviorTest.php ├── Item.php ├── ItemMerge.php ├── ItemRequired.php ├── TestMigration.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | 4 | # windows thumbnail cache 5 | Thumbs.db 6 | 7 | # composer vendor dir 8 | /vendor 9 | 10 | # composer itself is not needed 11 | composer.phar 12 | 13 | # Mac DS_Store Files 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | external_code_coverage: 3 | timeout: 2100 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | dist: trusty 3 | 4 | php: 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | - 7.1 9 | - 7.2 10 | - 7.3 11 | - 7.4 12 | 13 | sudo: false 14 | 15 | install: 16 | - composer self-update 17 | - composer update --prefer-dist --no-interaction 18 | 19 | before_script: 20 | - | 21 | if [ $TRAVIS_PHP_VERSION = '7.4' ]; then 22 | PHPUNIT_FLAGS="--coverage-clover=coverage.clover" 23 | fi 24 | 25 | script: 26 | - vendor/bin/phpunit $PHPUNIT_FLAGS 27 | 28 | after_script: 29 | - | 30 | if [ $TRAVIS_PHP_VERSION = '7.4' ]; then 31 | wget https://scrutinizer-ci.com/ocular.phar 32 | php ocular.phar code-coverage:upload --format=php-clover coverage.clover 33 | fi 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 PaulZi (pavel.zimakoff@gmail.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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii2 json attribute behavior 2 | 3 | Auto decode/encode attribute value in json, provide array access and json validator. 4 | 5 | **WARNING! From version 2.0.14 Yii has built-in DB JSON-type, and this behavior is no longer required.** 6 | 7 | [Russian readme](https://github.com/paulzi/yii2-json-behavior/blob/master/README.ru.md) 8 | 9 | [![Packagist Version](https://img.shields.io/packagist/v/paulzi/yii2-json-behavior.svg)](https://packagist.org/packages/paulzi/yii2-json-behavior) 10 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/paulzi/yii2-json-behavior/master.svg)](https://scrutinizer-ci.com/g/paulzi/yii2-json-behavior/?branch=master) 11 | [![Scrutinizer](https://img.shields.io/scrutinizer/g/paulzi/yii2-json-behavior.svg)](https://scrutinizer-ci.com/g/paulzi/yii2-json-behavior/?branch=master) 12 | [![Build Status](https://img.shields.io/travis/paulzi/yii2-json-behavior/master.svg)](https://travis-ci.org/paulzi/yii2-json-behavior) 13 | [![Total Downloads](https://img.shields.io/packagist/dt/paulzi/yii2-json-behavior.svg)](https://packagist.org/packages/paulzi/yii2-json-behavior) 14 | 15 | ## Install 16 | 17 | Install via Composer: 18 | 19 | ```bash 20 | composer require paulzi/yii2-json-behavior 21 | ``` 22 | 23 | or add 24 | 25 | ```bash 26 | "paulzi/yii2-json-behavior" : "~1.0.0" 27 | ``` 28 | 29 | to the `require` section of your `composer.json` file. 30 | 31 | ## Usage 32 | 33 | ### JsonBehavior 34 | 35 | Configure your model: 36 | 37 | ```php 38 | use paulzi\jsonBehavior\JsonBehavior; 39 | 40 | class Item extends \yii\db\ActiveRecord 41 | { 42 | public function behaviors() { 43 | return [ 44 | [ 45 | 'class' => JsonBehavior::className(), 46 | 'attributes' => ['params'], 47 | ], 48 | ]; 49 | } 50 | } 51 | ``` 52 | 53 | Now you can access to attribute as array: 54 | 55 | ```php 56 | $item = Item::findOne(1); 57 | $item->params['one'] = 'two'; 58 | $item->params['two'] = []; 59 | $item->params['two']['key'] = true; 60 | $item->save(); 61 | 62 | $item = Item::findOne(1); 63 | echo $item['two']['key']; // true 64 | ``` 65 | 66 | Set attribute via json string: 67 | 68 | ```php 69 | $item = new Item(); 70 | $item->params->set('[2, 4, 42]'); 71 | echo $item->params[2]; // 42 72 | ``` 73 | 74 | Set attribute via array: 75 | 76 | ```php 77 | $item = new Item(); 78 | $item->params->set(['test' => ['one' => 1]]); 79 | echo $item->params['test']['one']; // 1 80 | ``` 81 | 82 | Convert to json string: 83 | 84 | ```php 85 | $item = new Item(); 86 | $item->params['test'] = ['one' => false, 'two' => [1, 2, 3]]; 87 | var_dump((string)$item->params); // {"one":false,"two":[1,2,3]} 88 | ``` 89 | 90 | Convert to array: 91 | 92 | ```php 93 | $item = new Item(); 94 | $item->params->set('{ "one": 1, "two": null, "three": false, "four": "four" }'); 95 | var_dump($item->params->toArray()); 96 | ``` 97 | 98 | Check empty: 99 | 100 | ```php 101 | $item = new Item(); 102 | $item->params->set('{}'); 103 | var_dump($item->params->isEmpty()); // true 104 | ``` 105 | 106 | #### emptyValue 107 | 108 | You can set `emptyValue` option to define an empty JSON value (default `null`). Can be `'{}'`, `'[]''` or `null`. 109 | 110 | ### JsonValidator 111 | 112 | Configure your model (see behavior config upper): 113 | 114 | ```php 115 | use paulzi\jsonBehavior\JsonValidator; 116 | 117 | class Item extends \yii\db\ActiveRecord 118 | { 119 | public function rules() { 120 | return [ 121 | [['params'], JsonValidator::className()], 122 | ]; 123 | } 124 | } 125 | ``` 126 | 127 | Validate: 128 | 129 | ```php 130 | $item = new Item(); 131 | $item->attributes = ['params' => '{ test: }']; 132 | var_dump($item->save()); // false 133 | var_dump($item->errors); // ['params' => ['Value is not valid JSON or scalar']] 134 | ``` 135 | 136 | You can set `merge = true`, in this case, instead of replacing all field data of the transmitted data, `array_merge()` will be applied with old data in the field (which are taken from `oldAttributes` of ActiveRecord). This option can be apply only with ActiveRecord: 137 | 138 | ```php 139 | use paulzi\jsonBehavior\JsonValidator; 140 | 141 | class Item extends \yii\db\ActiveRecord 142 | { 143 | public function rules() { 144 | return [ 145 | [['params'], JsonValidator::className(), 'merge' => true], 146 | ]; 147 | } 148 | } 149 | ``` 150 | 151 | ### JsonField 152 | 153 | You can use `JsonField` class for other models: 154 | 155 | ```php 156 | class Item 157 | { 158 | public $params; 159 | 160 | public function __constructor() 161 | { 162 | $this->params = new JsonField(); 163 | } 164 | } 165 | 166 | // ... 167 | 168 | $item = new Item(); 169 | $item->params['one'] = 1; 170 | var_dump((string)$item->params); // {"one":1} 171 | ``` 172 | 173 | ## How To 174 | 175 | ### Usage isAttributeChanged() and getDirtyAttributes() 176 | 177 | Yii2 does not provide the ability to inject code to check attribute dirty. 178 | 179 | If you need to use methods `isAttributeChanged()` or `getDirtyAttributes()`, you can override them in model: 180 | 181 | ```php 182 | /** 183 | * @inheritdoc 184 | */ 185 | public function isAttributeChanged($name, $identical = true) 186 | { 187 | if ($this->$name instanceof JsonField) { 188 | return (string)$this->$name !== $this->getOldAttribute($name); 189 | } else { 190 | return parent::isAttributeChanged($name, $identical); 191 | } 192 | } 193 | 194 | /** 195 | * @inheritdoc 196 | */ 197 | public function getDirtyAttributes($names = null) 198 | { 199 | $result = []; 200 | $data = parent::getDirtyAttributes($names); 201 | foreach ($data as $name => $value) { 202 | if ($value instanceof JsonField) { 203 | if ((string)$value !== $this->getOldAttribute($name)) { 204 | $result[$name] = $value; 205 | } 206 | } else { 207 | $result[$name] = $value; 208 | } 209 | } 210 | return $result; 211 | } 212 | ``` -------------------------------------------------------------------------------- /README.ru.md: -------------------------------------------------------------------------------- 1 | # Yii2 json attribute behavior 2 | 3 | Автоматически призводит кодирование/декодирование атрибутов в формат json, предоставляет доступ как к массиву и валидацию json. 4 | 5 | **ВНИМАНИЕ! Начиная с версии 2.0.14 Yii имеет встроенную поддержку JSON-типов в БД, и данное поведение больше не будет работать с такими полями.** 6 | 7 | [English readme](https://github.com/paulzi/yii2-json-behavior/) 8 | 9 | [![Packagist Version](https://img.shields.io/packagist/v/paulzi/yii2-json-behavior.svg)](https://packagist.org/packages/paulzi/yii2-json-behavior) 10 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/paulzi/yii2-json-behavior/master.svg)](https://scrutinizer-ci.com/g/paulzi/yii2-json-behavior/?branch=master) 11 | [![Scrutinizer](https://img.shields.io/scrutinizer/g/paulzi/yii2-json-behavior.svg)](https://scrutinizer-ci.com/g/paulzi/yii2-json-behavior/?branch=master) 12 | [![Build Status](https://img.shields.io/travis/paulzi/yii2-json-behavior/master.svg)](https://travis-ci.org/paulzi/yii2-json-behavior) 13 | [![Total Downloads](https://img.shields.io/packagist/dt/paulzi/yii2-json-behavior.svg)](https://packagist.org/packages/paulzi/yii2-json-behavior) 14 | 15 | ## Установка 16 | 17 | Установка через Composer: 18 | 19 | ```bash 20 | composer require paulzi/yii2-json-behavior 21 | ``` 22 | 23 | или добавьте 24 | 25 | ```bash 26 | "paulzi/yii2-json-behavior" : "~1.0.0" 27 | ``` 28 | 29 | в секцию `require` в вашем `composer.json` файле. 30 | 31 | ## Использование 32 | 33 | ### JsonBehavior 34 | 35 | Настройте вашу модель: 36 | 37 | ```php 38 | use paulzi\jsonBehavior\JsonBehavior; 39 | 40 | class Item extends \yii\db\ActiveRecord 41 | { 42 | public function behaviors() { 43 | return [ 44 | [ 45 | 'class' => JsonBehavior::className(), 46 | 'attributes' => ['params'], 47 | ], 48 | ]; 49 | } 50 | } 51 | ``` 52 | 53 | Теперь вы можете обращаться к атрибуту как к массиву: 54 | 55 | ```php 56 | $item = Item::findOne(1); 57 | $item->params['one'] = 'two'; 58 | $item->params['two'] = []; 59 | $item->params['two']['key'] = true; 60 | $item->save(); 61 | 62 | $item = Item::findOne(1); 63 | echo $item['two']['key']; // true 64 | ``` 65 | 66 | Установка значения атрибута через JSON-строку: 67 | 68 | ```php 69 | $item = new Item(); 70 | $item->params->set('[2, 4, 42]'); 71 | echo $item->params[2]; // 42 72 | ``` 73 | 74 | Установка значения атрибута через массив: 75 | 76 | ```php 77 | $item = new Item(); 78 | $item->params->set(['test' => ['one' => 1]]); 79 | echo $item->params['test']['one']; // 1 80 | ``` 81 | 82 | Получение значения в виде JSON-строки: 83 | 84 | ```php 85 | $item = new Item(); 86 | $item->params['test'] = ['one' => false, 'two' => [1, 2, 3]]; 87 | var_dump((string)$item->params); // {"one":false,"two":[1,2,3]} 88 | ``` 89 | 90 | Получение значения в виде массива: 91 | 92 | ```php 93 | $item = new Item(); 94 | $item->params->set('{ "one": 1, "two": null, "three": false, "four": "four" }'); 95 | var_dump($item->params->toArray()); 96 | ``` 97 | 98 | Проверка на пустоту: 99 | 100 | ```php 101 | $item = new Item(); 102 | $item->params->set('{}'); 103 | var_dump($item->params->isEmpty()); // true 104 | ``` 105 | 106 | #### emptyValue 107 | 108 | Вы можете задать опцию `emptyValue` для определения значения для пустого JSON (по умолчанию `null`). Может принимать значения `'{}'`, `'[]''` или `null`. 109 | 110 | ### JsonValidator 111 | 112 | Настройте дополнительно модель (подключение поведения описано выше): 113 | 114 | ```php 115 | use paulzi\jsonBehavior\JsonValidator; 116 | 117 | class Item extends \yii\db\ActiveRecord 118 | { 119 | public function rules() { 120 | return [ 121 | [['params'], JsonValidator::className()], 122 | ]; 123 | } 124 | } 125 | ``` 126 | 127 | Валидация: 128 | 129 | ```php 130 | $item = new Item(); 131 | $item->attributes = ['params' => '{ test: }']; 132 | var_dump($item->save()); // false 133 | var_dump($item->errors); // ['params' => ['Value is not valid JSON or scalar']] 134 | ``` 135 | 136 | В качестве опции можно передать `merge = true`, в этом случае вместо замены всего значения поля переданными данными, будет осуществлен `array_merge()` со старыми данными в поле (которые берутся из `oldAttributes` ActiveRecord). Данный параметр применим только для ActiveRecord: 137 | 138 | ```php 139 | use paulzi\jsonBehavior\JsonValidator; 140 | 141 | class Item extends \yii\db\ActiveRecord 142 | { 143 | public function rules() { 144 | return [ 145 | [['params'], JsonValidator::className(), 'merge' => true], 146 | ]; 147 | } 148 | } 149 | ``` 150 | 151 | ### JsonField 152 | 153 | Вы можете использовать класс `JsonField` для других моделей: 154 | 155 | ```php 156 | class Item 157 | { 158 | public $params; 159 | 160 | public function __constructor() 161 | { 162 | $this->params = new JsonField(); 163 | } 164 | } 165 | 166 | // ... 167 | 168 | $item = new Item(); 169 | $item->params['one'] = 1; 170 | var_dump((string)$item->params); // {"one":1} 171 | ``` 172 | 173 | ## How To 174 | 175 | ### Использование методов isAttributeChanged() and getDirtyAttributes() 176 | 177 | Yii2 не предоставляет возможности для внедрения стороннего кода при выполнении проверки `dirty` атрибута. 178 | 179 | Если вам необходимо использовать методы `isAttributeChanged()` или `getDirtyAttributes()`, вы можете переопределить их в модели: 180 | 181 | ```php 182 | /** 183 | * @inheritdoc 184 | */ 185 | public function isAttributeChanged($name, $identical = true) 186 | { 187 | if ($this->$name instanceof JsonField) { 188 | return (string)$this->$name !== $this->getOldAttribute($name); 189 | } else { 190 | return parent::isAttributeChanged($name, $identical); 191 | } 192 | } 193 | 194 | /** 195 | * @inheritdoc 196 | */ 197 | public function getDirtyAttributes($names = null) 198 | { 199 | $result = []; 200 | $data = parent::getDirtyAttributes($names); 201 | foreach ($data as $name => $value) { 202 | if ($value instanceof JsonField) { 203 | if ((string)$value !== $this->getOldAttribute($name)) { 204 | $result[$name] = $value; 205 | } 206 | } else { 207 | $result[$name] = $value; 208 | } 209 | } 210 | return $result; 211 | } 212 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paulzi/yii2-json-behavior", 3 | "description": "Yii2 json attribute behavior", 4 | "keywords": ["yii2", "json", "behavior", "validator"], 5 | "type": "yii2-extension", 6 | "license": "MIT", 7 | "support": { 8 | "issues": "https://github.com/paulzi/yii2-json-behavior/issues?state=open", 9 | "source": "https://github.com/paulzi/yii2-json-behavior" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "PaulZi", 14 | "email": "pavel.zimakoff@gmail.com" 15 | } 16 | ], 17 | "minimum-stability": "stable", 18 | "require": { 19 | "php": ">=5.4.0", 20 | "yiisoft/yii2": "~2.0.0" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "~4.0" 24 | }, 25 | "repositories": [ 26 | { 27 | "type": "composer", 28 | "url": "https://asset-packagist.org" 29 | } 30 | ], 31 | "autoload": { 32 | "psr-4": { 33 | "paulzi\\jsonBehavior\\": "./src" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "c1bbc962c0fb59d89af3417a5710d157", 8 | "packages": [ 9 | { 10 | "name": "bower-asset/inputmask", 11 | "version": "3.3.10", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/RobinHerbots/Inputmask.git", 15 | "reference": "14873e5775964275d13621cbe2b52e4448af3707" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/RobinHerbots/Inputmask/zipball/14873e5775964275d13621cbe2b52e4448af3707", 20 | "reference": "14873e5775964275d13621cbe2b52e4448af3707", 21 | "shasum": null 22 | }, 23 | "require": { 24 | "bower-asset/jquery": ">=1.7" 25 | }, 26 | "type": "bower-asset", 27 | "license": [ 28 | "http://opensource.org/licenses/mit-license.php" 29 | ] 30 | }, 31 | { 32 | "name": "bower-asset/jquery", 33 | "version": "3.2.1", 34 | "source": { 35 | "type": "git", 36 | "url": "https://github.com/jquery/jquery-dist.git", 37 | "reference": "77d2a51d0520d2ee44173afdf4e40a9201f5964e" 38 | }, 39 | "dist": { 40 | "type": "zip", 41 | "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/77d2a51d0520d2ee44173afdf4e40a9201f5964e", 42 | "reference": "77d2a51d0520d2ee44173afdf4e40a9201f5964e", 43 | "shasum": null 44 | }, 45 | "type": "bower-asset", 46 | "license": [ 47 | "MIT" 48 | ] 49 | }, 50 | { 51 | "name": "bower-asset/punycode", 52 | "version": "v1.3.2", 53 | "source": { 54 | "type": "git", 55 | "url": "https://github.com/bestiejs/punycode.js.git", 56 | "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" 57 | }, 58 | "dist": { 59 | "type": "zip", 60 | "url": "https://api.github.com/repos/bestiejs/punycode.js/zipball/38c8d3131a82567bfef18da09f7f4db68c84f8a3", 61 | "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3", 62 | "shasum": "" 63 | }, 64 | "type": "bower-asset-library", 65 | "extra": { 66 | "bower-asset-main": "punycode.js", 67 | "bower-asset-ignore": [ 68 | "coverage", 69 | "tests", 70 | ".*", 71 | "component.json", 72 | "Gruntfile.js", 73 | "node_modules", 74 | "package.json" 75 | ] 76 | } 77 | }, 78 | { 79 | "name": "bower-asset/yii2-pjax", 80 | "version": "2.0.7.1", 81 | "source": { 82 | "type": "git", 83 | "url": "https://github.com/yiisoft/jquery-pjax.git", 84 | "reference": "aef7b953107264f00234902a3880eb50dafc48be" 85 | }, 86 | "dist": { 87 | "type": "zip", 88 | "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/aef7b953107264f00234902a3880eb50dafc48be", 89 | "reference": "aef7b953107264f00234902a3880eb50dafc48be", 90 | "shasum": null 91 | }, 92 | "require": { 93 | "bower-asset/jquery": ">=1.8" 94 | }, 95 | "type": "bower-asset", 96 | "license": [ 97 | "MIT" 98 | ] 99 | }, 100 | { 101 | "name": "cebe/markdown", 102 | "version": "1.1.2", 103 | "source": { 104 | "type": "git", 105 | "url": "https://github.com/cebe/markdown.git", 106 | "reference": "25b28bae8a6f185b5030673af77b32e1163d5c6e" 107 | }, 108 | "dist": { 109 | "type": "zip", 110 | "url": "https://api.github.com/repos/cebe/markdown/zipball/25b28bae8a6f185b5030673af77b32e1163d5c6e", 111 | "reference": "25b28bae8a6f185b5030673af77b32e1163d5c6e", 112 | "shasum": "" 113 | }, 114 | "require": { 115 | "lib-pcre": "*", 116 | "php": ">=5.4.0" 117 | }, 118 | "require-dev": { 119 | "cebe/indent": "*", 120 | "facebook/xhprof": "*@dev", 121 | "phpunit/phpunit": "4.1.*" 122 | }, 123 | "bin": [ 124 | "bin/markdown" 125 | ], 126 | "type": "library", 127 | "extra": { 128 | "branch-alias": { 129 | "dev-master": "1.1.x-dev" 130 | } 131 | }, 132 | "autoload": { 133 | "psr-4": { 134 | "cebe\\markdown\\": "" 135 | } 136 | }, 137 | "notification-url": "https://packagist.org/downloads/", 138 | "license": [ 139 | "MIT" 140 | ], 141 | "authors": [ 142 | { 143 | "name": "Carsten Brandt", 144 | "email": "mail@cebe.cc", 145 | "homepage": "http://cebe.cc/", 146 | "role": "Creator" 147 | } 148 | ], 149 | "description": "A super fast, highly extensible markdown parser for PHP", 150 | "homepage": "https://github.com/cebe/markdown#readme", 151 | "keywords": [ 152 | "extensible", 153 | "fast", 154 | "gfm", 155 | "markdown", 156 | "markdown-extra" 157 | ], 158 | "time": "2017-07-16T21:13:23+00:00" 159 | }, 160 | { 161 | "name": "ezyang/htmlpurifier", 162 | "version": "v4.9.3", 163 | "source": { 164 | "type": "git", 165 | "url": "https://github.com/ezyang/htmlpurifier.git", 166 | "reference": "95e1bae3182efc0f3422896a3236e991049dac69" 167 | }, 168 | "dist": { 169 | "type": "zip", 170 | "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/95e1bae3182efc0f3422896a3236e991049dac69", 171 | "reference": "95e1bae3182efc0f3422896a3236e991049dac69", 172 | "shasum": "" 173 | }, 174 | "require": { 175 | "php": ">=5.2" 176 | }, 177 | "require-dev": { 178 | "simpletest/simpletest": "^1.1" 179 | }, 180 | "type": "library", 181 | "autoload": { 182 | "psr-0": { 183 | "HTMLPurifier": "library/" 184 | }, 185 | "files": [ 186 | "library/HTMLPurifier.composer.php" 187 | ] 188 | }, 189 | "notification-url": "https://packagist.org/downloads/", 190 | "license": [ 191 | "LGPL" 192 | ], 193 | "authors": [ 194 | { 195 | "name": "Edward Z. Yang", 196 | "email": "admin@htmlpurifier.org", 197 | "homepage": "http://ezyang.com" 198 | } 199 | ], 200 | "description": "Standards compliant HTML filter written in PHP", 201 | "homepage": "http://htmlpurifier.org/", 202 | "keywords": [ 203 | "html" 204 | ], 205 | "time": "2017-06-03T02:28:16+00:00" 206 | }, 207 | { 208 | "name": "yiisoft/yii2", 209 | "version": "2.0.13", 210 | "source": { 211 | "type": "git", 212 | "url": "https://github.com/yiisoft/yii2-framework.git", 213 | "reference": "9f82b8f0df9adb4b8a410be5aaa97818153689d9" 214 | }, 215 | "dist": { 216 | "type": "zip", 217 | "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/9f82b8f0df9adb4b8a410be5aaa97818153689d9", 218 | "reference": "9f82b8f0df9adb4b8a410be5aaa97818153689d9", 219 | "shasum": "" 220 | }, 221 | "require": { 222 | "bower-asset/inputmask": "~3.2.2 | ~3.3.5", 223 | "bower-asset/jquery": "3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", 224 | "bower-asset/punycode": "1.3.*", 225 | "bower-asset/yii2-pjax": "~2.0.1", 226 | "cebe/markdown": "~1.0.0 | ~1.1.0", 227 | "ext-ctype": "*", 228 | "ext-mbstring": "*", 229 | "ezyang/htmlpurifier": "~4.6", 230 | "lib-pcre": "*", 231 | "php": ">=5.4.0", 232 | "yiisoft/yii2-composer": "~2.0.4" 233 | }, 234 | "bin": [ 235 | "yii" 236 | ], 237 | "type": "library", 238 | "extra": { 239 | "branch-alias": { 240 | "dev-master": "2.0.x-dev" 241 | } 242 | }, 243 | "autoload": { 244 | "psr-4": { 245 | "yii\\": "" 246 | } 247 | }, 248 | "notification-url": "https://packagist.org/downloads/", 249 | "license": [ 250 | "BSD-3-Clause" 251 | ], 252 | "authors": [ 253 | { 254 | "name": "Qiang Xue", 255 | "email": "qiang.xue@gmail.com", 256 | "homepage": "http://www.yiiframework.com/", 257 | "role": "Founder and project lead" 258 | }, 259 | { 260 | "name": "Alexander Makarov", 261 | "email": "sam@rmcreative.ru", 262 | "homepage": "http://rmcreative.ru/", 263 | "role": "Core framework development" 264 | }, 265 | { 266 | "name": "Maurizio Domba", 267 | "homepage": "http://mdomba.info/", 268 | "role": "Core framework development" 269 | }, 270 | { 271 | "name": "Carsten Brandt", 272 | "email": "mail@cebe.cc", 273 | "homepage": "http://cebe.cc/", 274 | "role": "Core framework development" 275 | }, 276 | { 277 | "name": "Timur Ruziev", 278 | "email": "resurtm@gmail.com", 279 | "homepage": "http://resurtm.com/", 280 | "role": "Core framework development" 281 | }, 282 | { 283 | "name": "Paul Klimov", 284 | "email": "klimov.paul@gmail.com", 285 | "role": "Core framework development" 286 | }, 287 | { 288 | "name": "Dmitry Naumenko", 289 | "email": "d.naumenko.a@gmail.com", 290 | "role": "Core framework development" 291 | }, 292 | { 293 | "name": "Boudewijn Vahrmeijer", 294 | "email": "info@dynasource.eu", 295 | "homepage": "http://dynasource.eu", 296 | "role": "Core framework development" 297 | } 298 | ], 299 | "description": "Yii PHP Framework Version 2", 300 | "homepage": "http://www.yiiframework.com/", 301 | "keywords": [ 302 | "framework", 303 | "yii2" 304 | ], 305 | "time": "2017-11-02T22:09:29+00:00" 306 | }, 307 | { 308 | "name": "yiisoft/yii2-composer", 309 | "version": "2.0.5", 310 | "source": { 311 | "type": "git", 312 | "url": "https://github.com/yiisoft/yii2-composer.git", 313 | "reference": "3f4923c2bde6caf3f5b88cc22fdd5770f52f8df2" 314 | }, 315 | "dist": { 316 | "type": "zip", 317 | "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/3f4923c2bde6caf3f5b88cc22fdd5770f52f8df2", 318 | "reference": "3f4923c2bde6caf3f5b88cc22fdd5770f52f8df2", 319 | "shasum": "" 320 | }, 321 | "require": { 322 | "composer-plugin-api": "^1.0" 323 | }, 324 | "require-dev": { 325 | "composer/composer": "^1.0" 326 | }, 327 | "type": "composer-plugin", 328 | "extra": { 329 | "class": "yii\\composer\\Plugin", 330 | "branch-alias": { 331 | "dev-master": "2.0.x-dev" 332 | } 333 | }, 334 | "autoload": { 335 | "psr-4": { 336 | "yii\\composer\\": "" 337 | } 338 | }, 339 | "notification-url": "https://packagist.org/downloads/", 340 | "license": [ 341 | "BSD-3-Clause" 342 | ], 343 | "authors": [ 344 | { 345 | "name": "Qiang Xue", 346 | "email": "qiang.xue@gmail.com" 347 | } 348 | ], 349 | "description": "The composer plugin for Yii extension installer", 350 | "keywords": [ 351 | "composer", 352 | "extension installer", 353 | "yii2" 354 | ], 355 | "time": "2016-12-20T13:26:02+00:00" 356 | } 357 | ], 358 | "packages-dev": [ 359 | { 360 | "name": "doctrine/instantiator", 361 | "version": "1.0.5", 362 | "source": { 363 | "type": "git", 364 | "url": "https://github.com/doctrine/instantiator.git", 365 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 366 | }, 367 | "dist": { 368 | "type": "zip", 369 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 370 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 371 | "shasum": "" 372 | }, 373 | "require": { 374 | "php": ">=5.3,<8.0-DEV" 375 | }, 376 | "require-dev": { 377 | "athletic/athletic": "~0.1.8", 378 | "ext-pdo": "*", 379 | "ext-phar": "*", 380 | "phpunit/phpunit": "~4.0", 381 | "squizlabs/php_codesniffer": "~2.0" 382 | }, 383 | "type": "library", 384 | "extra": { 385 | "branch-alias": { 386 | "dev-master": "1.0.x-dev" 387 | } 388 | }, 389 | "autoload": { 390 | "psr-4": { 391 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 392 | } 393 | }, 394 | "notification-url": "https://packagist.org/downloads/", 395 | "license": [ 396 | "MIT" 397 | ], 398 | "authors": [ 399 | { 400 | "name": "Marco Pivetta", 401 | "email": "ocramius@gmail.com", 402 | "homepage": "http://ocramius.github.com/" 403 | } 404 | ], 405 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 406 | "homepage": "https://github.com/doctrine/instantiator", 407 | "keywords": [ 408 | "constructor", 409 | "instantiate" 410 | ], 411 | "time": "2015-06-14T21:17:01+00:00" 412 | }, 413 | { 414 | "name": "phpdocumentor/reflection-common", 415 | "version": "1.0.1", 416 | "source": { 417 | "type": "git", 418 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 419 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 420 | }, 421 | "dist": { 422 | "type": "zip", 423 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 424 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 425 | "shasum": "" 426 | }, 427 | "require": { 428 | "php": ">=5.5" 429 | }, 430 | "require-dev": { 431 | "phpunit/phpunit": "^4.6" 432 | }, 433 | "type": "library", 434 | "extra": { 435 | "branch-alias": { 436 | "dev-master": "1.0.x-dev" 437 | } 438 | }, 439 | "autoload": { 440 | "psr-4": { 441 | "phpDocumentor\\Reflection\\": [ 442 | "src" 443 | ] 444 | } 445 | }, 446 | "notification-url": "https://packagist.org/downloads/", 447 | "license": [ 448 | "MIT" 449 | ], 450 | "authors": [ 451 | { 452 | "name": "Jaap van Otterdijk", 453 | "email": "opensource@ijaap.nl" 454 | } 455 | ], 456 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 457 | "homepage": "http://www.phpdoc.org", 458 | "keywords": [ 459 | "FQSEN", 460 | "phpDocumentor", 461 | "phpdoc", 462 | "reflection", 463 | "static analysis" 464 | ], 465 | "time": "2017-09-11T18:02:19+00:00" 466 | }, 467 | { 468 | "name": "phpdocumentor/reflection-docblock", 469 | "version": "4.1.1", 470 | "source": { 471 | "type": "git", 472 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 473 | "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" 474 | }, 475 | "dist": { 476 | "type": "zip", 477 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", 478 | "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", 479 | "shasum": "" 480 | }, 481 | "require": { 482 | "php": "^7.0", 483 | "phpdocumentor/reflection-common": "^1.0@dev", 484 | "phpdocumentor/type-resolver": "^0.4.0", 485 | "webmozart/assert": "^1.0" 486 | }, 487 | "require-dev": { 488 | "mockery/mockery": "^0.9.4", 489 | "phpunit/phpunit": "^4.4" 490 | }, 491 | "type": "library", 492 | "autoload": { 493 | "psr-4": { 494 | "phpDocumentor\\Reflection\\": [ 495 | "src/" 496 | ] 497 | } 498 | }, 499 | "notification-url": "https://packagist.org/downloads/", 500 | "license": [ 501 | "MIT" 502 | ], 503 | "authors": [ 504 | { 505 | "name": "Mike van Riel", 506 | "email": "me@mikevanriel.com" 507 | } 508 | ], 509 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 510 | "time": "2017-08-30T18:51:59+00:00" 511 | }, 512 | { 513 | "name": "phpdocumentor/type-resolver", 514 | "version": "0.4.0", 515 | "source": { 516 | "type": "git", 517 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 518 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 519 | }, 520 | "dist": { 521 | "type": "zip", 522 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 523 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 524 | "shasum": "" 525 | }, 526 | "require": { 527 | "php": "^5.5 || ^7.0", 528 | "phpdocumentor/reflection-common": "^1.0" 529 | }, 530 | "require-dev": { 531 | "mockery/mockery": "^0.9.4", 532 | "phpunit/phpunit": "^5.2||^4.8.24" 533 | }, 534 | "type": "library", 535 | "extra": { 536 | "branch-alias": { 537 | "dev-master": "1.0.x-dev" 538 | } 539 | }, 540 | "autoload": { 541 | "psr-4": { 542 | "phpDocumentor\\Reflection\\": [ 543 | "src/" 544 | ] 545 | } 546 | }, 547 | "notification-url": "https://packagist.org/downloads/", 548 | "license": [ 549 | "MIT" 550 | ], 551 | "authors": [ 552 | { 553 | "name": "Mike van Riel", 554 | "email": "me@mikevanriel.com" 555 | } 556 | ], 557 | "time": "2017-07-14T14:27:02+00:00" 558 | }, 559 | { 560 | "name": "phpspec/prophecy", 561 | "version": "v1.7.2", 562 | "source": { 563 | "type": "git", 564 | "url": "https://github.com/phpspec/prophecy.git", 565 | "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" 566 | }, 567 | "dist": { 568 | "type": "zip", 569 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", 570 | "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", 571 | "shasum": "" 572 | }, 573 | "require": { 574 | "doctrine/instantiator": "^1.0.2", 575 | "php": "^5.3|^7.0", 576 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 577 | "sebastian/comparator": "^1.1|^2.0", 578 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 579 | }, 580 | "require-dev": { 581 | "phpspec/phpspec": "^2.5|^3.2", 582 | "phpunit/phpunit": "^4.8 || ^5.6.5" 583 | }, 584 | "type": "library", 585 | "extra": { 586 | "branch-alias": { 587 | "dev-master": "1.7.x-dev" 588 | } 589 | }, 590 | "autoload": { 591 | "psr-0": { 592 | "Prophecy\\": "src/" 593 | } 594 | }, 595 | "notification-url": "https://packagist.org/downloads/", 596 | "license": [ 597 | "MIT" 598 | ], 599 | "authors": [ 600 | { 601 | "name": "Konstantin Kudryashov", 602 | "email": "ever.zet@gmail.com", 603 | "homepage": "http://everzet.com" 604 | }, 605 | { 606 | "name": "Marcello Duarte", 607 | "email": "marcello.duarte@gmail.com" 608 | } 609 | ], 610 | "description": "Highly opinionated mocking framework for PHP 5.3+", 611 | "homepage": "https://github.com/phpspec/prophecy", 612 | "keywords": [ 613 | "Double", 614 | "Dummy", 615 | "fake", 616 | "mock", 617 | "spy", 618 | "stub" 619 | ], 620 | "time": "2017-09-04T11:05:03+00:00" 621 | }, 622 | { 623 | "name": "phpunit/php-code-coverage", 624 | "version": "2.2.4", 625 | "source": { 626 | "type": "git", 627 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 628 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 629 | }, 630 | "dist": { 631 | "type": "zip", 632 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 633 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 634 | "shasum": "" 635 | }, 636 | "require": { 637 | "php": ">=5.3.3", 638 | "phpunit/php-file-iterator": "~1.3", 639 | "phpunit/php-text-template": "~1.2", 640 | "phpunit/php-token-stream": "~1.3", 641 | "sebastian/environment": "^1.3.2", 642 | "sebastian/version": "~1.0" 643 | }, 644 | "require-dev": { 645 | "ext-xdebug": ">=2.1.4", 646 | "phpunit/phpunit": "~4" 647 | }, 648 | "suggest": { 649 | "ext-dom": "*", 650 | "ext-xdebug": ">=2.2.1", 651 | "ext-xmlwriter": "*" 652 | }, 653 | "type": "library", 654 | "extra": { 655 | "branch-alias": { 656 | "dev-master": "2.2.x-dev" 657 | } 658 | }, 659 | "autoload": { 660 | "classmap": [ 661 | "src/" 662 | ] 663 | }, 664 | "notification-url": "https://packagist.org/downloads/", 665 | "license": [ 666 | "BSD-3-Clause" 667 | ], 668 | "authors": [ 669 | { 670 | "name": "Sebastian Bergmann", 671 | "email": "sb@sebastian-bergmann.de", 672 | "role": "lead" 673 | } 674 | ], 675 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 676 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 677 | "keywords": [ 678 | "coverage", 679 | "testing", 680 | "xunit" 681 | ], 682 | "time": "2015-10-06T15:47:00+00:00" 683 | }, 684 | { 685 | "name": "phpunit/php-file-iterator", 686 | "version": "1.4.2", 687 | "source": { 688 | "type": "git", 689 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 690 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" 691 | }, 692 | "dist": { 693 | "type": "zip", 694 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 695 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 696 | "shasum": "" 697 | }, 698 | "require": { 699 | "php": ">=5.3.3" 700 | }, 701 | "type": "library", 702 | "extra": { 703 | "branch-alias": { 704 | "dev-master": "1.4.x-dev" 705 | } 706 | }, 707 | "autoload": { 708 | "classmap": [ 709 | "src/" 710 | ] 711 | }, 712 | "notification-url": "https://packagist.org/downloads/", 713 | "license": [ 714 | "BSD-3-Clause" 715 | ], 716 | "authors": [ 717 | { 718 | "name": "Sebastian Bergmann", 719 | "email": "sb@sebastian-bergmann.de", 720 | "role": "lead" 721 | } 722 | ], 723 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 724 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 725 | "keywords": [ 726 | "filesystem", 727 | "iterator" 728 | ], 729 | "time": "2016-10-03T07:40:28+00:00" 730 | }, 731 | { 732 | "name": "phpunit/php-text-template", 733 | "version": "1.2.1", 734 | "source": { 735 | "type": "git", 736 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 737 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 738 | }, 739 | "dist": { 740 | "type": "zip", 741 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 742 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 743 | "shasum": "" 744 | }, 745 | "require": { 746 | "php": ">=5.3.3" 747 | }, 748 | "type": "library", 749 | "autoload": { 750 | "classmap": [ 751 | "src/" 752 | ] 753 | }, 754 | "notification-url": "https://packagist.org/downloads/", 755 | "license": [ 756 | "BSD-3-Clause" 757 | ], 758 | "authors": [ 759 | { 760 | "name": "Sebastian Bergmann", 761 | "email": "sebastian@phpunit.de", 762 | "role": "lead" 763 | } 764 | ], 765 | "description": "Simple template engine.", 766 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 767 | "keywords": [ 768 | "template" 769 | ], 770 | "time": "2015-06-21T13:50:34+00:00" 771 | }, 772 | { 773 | "name": "phpunit/php-timer", 774 | "version": "1.0.9", 775 | "source": { 776 | "type": "git", 777 | "url": "https://github.com/sebastianbergmann/php-timer.git", 778 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 779 | }, 780 | "dist": { 781 | "type": "zip", 782 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 783 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 784 | "shasum": "" 785 | }, 786 | "require": { 787 | "php": "^5.3.3 || ^7.0" 788 | }, 789 | "require-dev": { 790 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 791 | }, 792 | "type": "library", 793 | "extra": { 794 | "branch-alias": { 795 | "dev-master": "1.0-dev" 796 | } 797 | }, 798 | "autoload": { 799 | "classmap": [ 800 | "src/" 801 | ] 802 | }, 803 | "notification-url": "https://packagist.org/downloads/", 804 | "license": [ 805 | "BSD-3-Clause" 806 | ], 807 | "authors": [ 808 | { 809 | "name": "Sebastian Bergmann", 810 | "email": "sb@sebastian-bergmann.de", 811 | "role": "lead" 812 | } 813 | ], 814 | "description": "Utility class for timing", 815 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 816 | "keywords": [ 817 | "timer" 818 | ], 819 | "time": "2017-02-26T11:10:40+00:00" 820 | }, 821 | { 822 | "name": "phpunit/php-token-stream", 823 | "version": "1.4.11", 824 | "source": { 825 | "type": "git", 826 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 827 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" 828 | }, 829 | "dist": { 830 | "type": "zip", 831 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", 832 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", 833 | "shasum": "" 834 | }, 835 | "require": { 836 | "ext-tokenizer": "*", 837 | "php": ">=5.3.3" 838 | }, 839 | "require-dev": { 840 | "phpunit/phpunit": "~4.2" 841 | }, 842 | "type": "library", 843 | "extra": { 844 | "branch-alias": { 845 | "dev-master": "1.4-dev" 846 | } 847 | }, 848 | "autoload": { 849 | "classmap": [ 850 | "src/" 851 | ] 852 | }, 853 | "notification-url": "https://packagist.org/downloads/", 854 | "license": [ 855 | "BSD-3-Clause" 856 | ], 857 | "authors": [ 858 | { 859 | "name": "Sebastian Bergmann", 860 | "email": "sebastian@phpunit.de" 861 | } 862 | ], 863 | "description": "Wrapper around PHP's tokenizer extension.", 864 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 865 | "keywords": [ 866 | "tokenizer" 867 | ], 868 | "time": "2017-02-27T10:12:30+00:00" 869 | }, 870 | { 871 | "name": "phpunit/phpunit", 872 | "version": "4.8.36", 873 | "source": { 874 | "type": "git", 875 | "url": "https://github.com/sebastianbergmann/phpunit.git", 876 | "reference": "46023de9a91eec7dfb06cc56cb4e260017298517" 877 | }, 878 | "dist": { 879 | "type": "zip", 880 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517", 881 | "reference": "46023de9a91eec7dfb06cc56cb4e260017298517", 882 | "shasum": "" 883 | }, 884 | "require": { 885 | "ext-dom": "*", 886 | "ext-json": "*", 887 | "ext-pcre": "*", 888 | "ext-reflection": "*", 889 | "ext-spl": "*", 890 | "php": ">=5.3.3", 891 | "phpspec/prophecy": "^1.3.1", 892 | "phpunit/php-code-coverage": "~2.1", 893 | "phpunit/php-file-iterator": "~1.4", 894 | "phpunit/php-text-template": "~1.2", 895 | "phpunit/php-timer": "^1.0.6", 896 | "phpunit/phpunit-mock-objects": "~2.3", 897 | "sebastian/comparator": "~1.2.2", 898 | "sebastian/diff": "~1.2", 899 | "sebastian/environment": "~1.3", 900 | "sebastian/exporter": "~1.2", 901 | "sebastian/global-state": "~1.0", 902 | "sebastian/version": "~1.0", 903 | "symfony/yaml": "~2.1|~3.0" 904 | }, 905 | "suggest": { 906 | "phpunit/php-invoker": "~1.1" 907 | }, 908 | "bin": [ 909 | "phpunit" 910 | ], 911 | "type": "library", 912 | "extra": { 913 | "branch-alias": { 914 | "dev-master": "4.8.x-dev" 915 | } 916 | }, 917 | "autoload": { 918 | "classmap": [ 919 | "src/" 920 | ] 921 | }, 922 | "notification-url": "https://packagist.org/downloads/", 923 | "license": [ 924 | "BSD-3-Clause" 925 | ], 926 | "authors": [ 927 | { 928 | "name": "Sebastian Bergmann", 929 | "email": "sebastian@phpunit.de", 930 | "role": "lead" 931 | } 932 | ], 933 | "description": "The PHP Unit Testing framework.", 934 | "homepage": "https://phpunit.de/", 935 | "keywords": [ 936 | "phpunit", 937 | "testing", 938 | "xunit" 939 | ], 940 | "time": "2017-06-21T08:07:12+00:00" 941 | }, 942 | { 943 | "name": "phpunit/phpunit-mock-objects", 944 | "version": "2.3.8", 945 | "source": { 946 | "type": "git", 947 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 948 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 949 | }, 950 | "dist": { 951 | "type": "zip", 952 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 953 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 954 | "shasum": "" 955 | }, 956 | "require": { 957 | "doctrine/instantiator": "^1.0.2", 958 | "php": ">=5.3.3", 959 | "phpunit/php-text-template": "~1.2", 960 | "sebastian/exporter": "~1.2" 961 | }, 962 | "require-dev": { 963 | "phpunit/phpunit": "~4.4" 964 | }, 965 | "suggest": { 966 | "ext-soap": "*" 967 | }, 968 | "type": "library", 969 | "extra": { 970 | "branch-alias": { 971 | "dev-master": "2.3.x-dev" 972 | } 973 | }, 974 | "autoload": { 975 | "classmap": [ 976 | "src/" 977 | ] 978 | }, 979 | "notification-url": "https://packagist.org/downloads/", 980 | "license": [ 981 | "BSD-3-Clause" 982 | ], 983 | "authors": [ 984 | { 985 | "name": "Sebastian Bergmann", 986 | "email": "sb@sebastian-bergmann.de", 987 | "role": "lead" 988 | } 989 | ], 990 | "description": "Mock Object library for PHPUnit", 991 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 992 | "keywords": [ 993 | "mock", 994 | "xunit" 995 | ], 996 | "time": "2015-10-02T06:51:40+00:00" 997 | }, 998 | { 999 | "name": "sebastian/comparator", 1000 | "version": "1.2.4", 1001 | "source": { 1002 | "type": "git", 1003 | "url": "https://github.com/sebastianbergmann/comparator.git", 1004 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" 1005 | }, 1006 | "dist": { 1007 | "type": "zip", 1008 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1009 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1010 | "shasum": "" 1011 | }, 1012 | "require": { 1013 | "php": ">=5.3.3", 1014 | "sebastian/diff": "~1.2", 1015 | "sebastian/exporter": "~1.2 || ~2.0" 1016 | }, 1017 | "require-dev": { 1018 | "phpunit/phpunit": "~4.4" 1019 | }, 1020 | "type": "library", 1021 | "extra": { 1022 | "branch-alias": { 1023 | "dev-master": "1.2.x-dev" 1024 | } 1025 | }, 1026 | "autoload": { 1027 | "classmap": [ 1028 | "src/" 1029 | ] 1030 | }, 1031 | "notification-url": "https://packagist.org/downloads/", 1032 | "license": [ 1033 | "BSD-3-Clause" 1034 | ], 1035 | "authors": [ 1036 | { 1037 | "name": "Jeff Welch", 1038 | "email": "whatthejeff@gmail.com" 1039 | }, 1040 | { 1041 | "name": "Volker Dusch", 1042 | "email": "github@wallbash.com" 1043 | }, 1044 | { 1045 | "name": "Bernhard Schussek", 1046 | "email": "bschussek@2bepublished.at" 1047 | }, 1048 | { 1049 | "name": "Sebastian Bergmann", 1050 | "email": "sebastian@phpunit.de" 1051 | } 1052 | ], 1053 | "description": "Provides the functionality to compare PHP values for equality", 1054 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1055 | "keywords": [ 1056 | "comparator", 1057 | "compare", 1058 | "equality" 1059 | ], 1060 | "time": "2017-01-29T09:50:25+00:00" 1061 | }, 1062 | { 1063 | "name": "sebastian/diff", 1064 | "version": "1.4.3", 1065 | "source": { 1066 | "type": "git", 1067 | "url": "https://github.com/sebastianbergmann/diff.git", 1068 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" 1069 | }, 1070 | "dist": { 1071 | "type": "zip", 1072 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", 1073 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", 1074 | "shasum": "" 1075 | }, 1076 | "require": { 1077 | "php": "^5.3.3 || ^7.0" 1078 | }, 1079 | "require-dev": { 1080 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 1081 | }, 1082 | "type": "library", 1083 | "extra": { 1084 | "branch-alias": { 1085 | "dev-master": "1.4-dev" 1086 | } 1087 | }, 1088 | "autoload": { 1089 | "classmap": [ 1090 | "src/" 1091 | ] 1092 | }, 1093 | "notification-url": "https://packagist.org/downloads/", 1094 | "license": [ 1095 | "BSD-3-Clause" 1096 | ], 1097 | "authors": [ 1098 | { 1099 | "name": "Kore Nordmann", 1100 | "email": "mail@kore-nordmann.de" 1101 | }, 1102 | { 1103 | "name": "Sebastian Bergmann", 1104 | "email": "sebastian@phpunit.de" 1105 | } 1106 | ], 1107 | "description": "Diff implementation", 1108 | "homepage": "https://github.com/sebastianbergmann/diff", 1109 | "keywords": [ 1110 | "diff" 1111 | ], 1112 | "time": "2017-05-22T07:24:03+00:00" 1113 | }, 1114 | { 1115 | "name": "sebastian/environment", 1116 | "version": "1.3.8", 1117 | "source": { 1118 | "type": "git", 1119 | "url": "https://github.com/sebastianbergmann/environment.git", 1120 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" 1121 | }, 1122 | "dist": { 1123 | "type": "zip", 1124 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", 1125 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", 1126 | "shasum": "" 1127 | }, 1128 | "require": { 1129 | "php": "^5.3.3 || ^7.0" 1130 | }, 1131 | "require-dev": { 1132 | "phpunit/phpunit": "^4.8 || ^5.0" 1133 | }, 1134 | "type": "library", 1135 | "extra": { 1136 | "branch-alias": { 1137 | "dev-master": "1.3.x-dev" 1138 | } 1139 | }, 1140 | "autoload": { 1141 | "classmap": [ 1142 | "src/" 1143 | ] 1144 | }, 1145 | "notification-url": "https://packagist.org/downloads/", 1146 | "license": [ 1147 | "BSD-3-Clause" 1148 | ], 1149 | "authors": [ 1150 | { 1151 | "name": "Sebastian Bergmann", 1152 | "email": "sebastian@phpunit.de" 1153 | } 1154 | ], 1155 | "description": "Provides functionality to handle HHVM/PHP environments", 1156 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1157 | "keywords": [ 1158 | "Xdebug", 1159 | "environment", 1160 | "hhvm" 1161 | ], 1162 | "time": "2016-08-18T05:49:44+00:00" 1163 | }, 1164 | { 1165 | "name": "sebastian/exporter", 1166 | "version": "1.2.2", 1167 | "source": { 1168 | "type": "git", 1169 | "url": "https://github.com/sebastianbergmann/exporter.git", 1170 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" 1171 | }, 1172 | "dist": { 1173 | "type": "zip", 1174 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", 1175 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", 1176 | "shasum": "" 1177 | }, 1178 | "require": { 1179 | "php": ">=5.3.3", 1180 | "sebastian/recursion-context": "~1.0" 1181 | }, 1182 | "require-dev": { 1183 | "ext-mbstring": "*", 1184 | "phpunit/phpunit": "~4.4" 1185 | }, 1186 | "type": "library", 1187 | "extra": { 1188 | "branch-alias": { 1189 | "dev-master": "1.3.x-dev" 1190 | } 1191 | }, 1192 | "autoload": { 1193 | "classmap": [ 1194 | "src/" 1195 | ] 1196 | }, 1197 | "notification-url": "https://packagist.org/downloads/", 1198 | "license": [ 1199 | "BSD-3-Clause" 1200 | ], 1201 | "authors": [ 1202 | { 1203 | "name": "Jeff Welch", 1204 | "email": "whatthejeff@gmail.com" 1205 | }, 1206 | { 1207 | "name": "Volker Dusch", 1208 | "email": "github@wallbash.com" 1209 | }, 1210 | { 1211 | "name": "Bernhard Schussek", 1212 | "email": "bschussek@2bepublished.at" 1213 | }, 1214 | { 1215 | "name": "Sebastian Bergmann", 1216 | "email": "sebastian@phpunit.de" 1217 | }, 1218 | { 1219 | "name": "Adam Harvey", 1220 | "email": "aharvey@php.net" 1221 | } 1222 | ], 1223 | "description": "Provides the functionality to export PHP variables for visualization", 1224 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1225 | "keywords": [ 1226 | "export", 1227 | "exporter" 1228 | ], 1229 | "time": "2016-06-17T09:04:28+00:00" 1230 | }, 1231 | { 1232 | "name": "sebastian/global-state", 1233 | "version": "1.1.1", 1234 | "source": { 1235 | "type": "git", 1236 | "url": "https://github.com/sebastianbergmann/global-state.git", 1237 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1238 | }, 1239 | "dist": { 1240 | "type": "zip", 1241 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1242 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1243 | "shasum": "" 1244 | }, 1245 | "require": { 1246 | "php": ">=5.3.3" 1247 | }, 1248 | "require-dev": { 1249 | "phpunit/phpunit": "~4.2" 1250 | }, 1251 | "suggest": { 1252 | "ext-uopz": "*" 1253 | }, 1254 | "type": "library", 1255 | "extra": { 1256 | "branch-alias": { 1257 | "dev-master": "1.0-dev" 1258 | } 1259 | }, 1260 | "autoload": { 1261 | "classmap": [ 1262 | "src/" 1263 | ] 1264 | }, 1265 | "notification-url": "https://packagist.org/downloads/", 1266 | "license": [ 1267 | "BSD-3-Clause" 1268 | ], 1269 | "authors": [ 1270 | { 1271 | "name": "Sebastian Bergmann", 1272 | "email": "sebastian@phpunit.de" 1273 | } 1274 | ], 1275 | "description": "Snapshotting of global state", 1276 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1277 | "keywords": [ 1278 | "global state" 1279 | ], 1280 | "time": "2015-10-12T03:26:01+00:00" 1281 | }, 1282 | { 1283 | "name": "sebastian/recursion-context", 1284 | "version": "1.0.5", 1285 | "source": { 1286 | "type": "git", 1287 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1288 | "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" 1289 | }, 1290 | "dist": { 1291 | "type": "zip", 1292 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", 1293 | "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", 1294 | "shasum": "" 1295 | }, 1296 | "require": { 1297 | "php": ">=5.3.3" 1298 | }, 1299 | "require-dev": { 1300 | "phpunit/phpunit": "~4.4" 1301 | }, 1302 | "type": "library", 1303 | "extra": { 1304 | "branch-alias": { 1305 | "dev-master": "1.0.x-dev" 1306 | } 1307 | }, 1308 | "autoload": { 1309 | "classmap": [ 1310 | "src/" 1311 | ] 1312 | }, 1313 | "notification-url": "https://packagist.org/downloads/", 1314 | "license": [ 1315 | "BSD-3-Clause" 1316 | ], 1317 | "authors": [ 1318 | { 1319 | "name": "Jeff Welch", 1320 | "email": "whatthejeff@gmail.com" 1321 | }, 1322 | { 1323 | "name": "Sebastian Bergmann", 1324 | "email": "sebastian@phpunit.de" 1325 | }, 1326 | { 1327 | "name": "Adam Harvey", 1328 | "email": "aharvey@php.net" 1329 | } 1330 | ], 1331 | "description": "Provides functionality to recursively process PHP variables", 1332 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1333 | "time": "2016-10-03T07:41:43+00:00" 1334 | }, 1335 | { 1336 | "name": "sebastian/version", 1337 | "version": "1.0.6", 1338 | "source": { 1339 | "type": "git", 1340 | "url": "https://github.com/sebastianbergmann/version.git", 1341 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1342 | }, 1343 | "dist": { 1344 | "type": "zip", 1345 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1346 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1347 | "shasum": "" 1348 | }, 1349 | "type": "library", 1350 | "autoload": { 1351 | "classmap": [ 1352 | "src/" 1353 | ] 1354 | }, 1355 | "notification-url": "https://packagist.org/downloads/", 1356 | "license": [ 1357 | "BSD-3-Clause" 1358 | ], 1359 | "authors": [ 1360 | { 1361 | "name": "Sebastian Bergmann", 1362 | "email": "sebastian@phpunit.de", 1363 | "role": "lead" 1364 | } 1365 | ], 1366 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1367 | "homepage": "https://github.com/sebastianbergmann/version", 1368 | "time": "2015-06-21T13:59:46+00:00" 1369 | }, 1370 | { 1371 | "name": "symfony/yaml", 1372 | "version": "v3.3.10", 1373 | "source": { 1374 | "type": "git", 1375 | "url": "https://github.com/symfony/yaml.git", 1376 | "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46" 1377 | }, 1378 | "dist": { 1379 | "type": "zip", 1380 | "url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", 1381 | "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", 1382 | "shasum": "" 1383 | }, 1384 | "require": { 1385 | "php": "^5.5.9|>=7.0.8" 1386 | }, 1387 | "require-dev": { 1388 | "symfony/console": "~2.8|~3.0" 1389 | }, 1390 | "suggest": { 1391 | "symfony/console": "For validating YAML files using the lint command" 1392 | }, 1393 | "type": "library", 1394 | "extra": { 1395 | "branch-alias": { 1396 | "dev-master": "3.3-dev" 1397 | } 1398 | }, 1399 | "autoload": { 1400 | "psr-4": { 1401 | "Symfony\\Component\\Yaml\\": "" 1402 | }, 1403 | "exclude-from-classmap": [ 1404 | "/Tests/" 1405 | ] 1406 | }, 1407 | "notification-url": "https://packagist.org/downloads/", 1408 | "license": [ 1409 | "MIT" 1410 | ], 1411 | "authors": [ 1412 | { 1413 | "name": "Fabien Potencier", 1414 | "email": "fabien@symfony.com" 1415 | }, 1416 | { 1417 | "name": "Symfony Community", 1418 | "homepage": "https://symfony.com/contributors" 1419 | } 1420 | ], 1421 | "description": "Symfony Yaml Component", 1422 | "homepage": "https://symfony.com", 1423 | "time": "2017-10-05T14:43:42+00:00" 1424 | }, 1425 | { 1426 | "name": "webmozart/assert", 1427 | "version": "1.2.0", 1428 | "source": { 1429 | "type": "git", 1430 | "url": "https://github.com/webmozart/assert.git", 1431 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" 1432 | }, 1433 | "dist": { 1434 | "type": "zip", 1435 | "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", 1436 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", 1437 | "shasum": "" 1438 | }, 1439 | "require": { 1440 | "php": "^5.3.3 || ^7.0" 1441 | }, 1442 | "require-dev": { 1443 | "phpunit/phpunit": "^4.6", 1444 | "sebastian/version": "^1.0.1" 1445 | }, 1446 | "type": "library", 1447 | "extra": { 1448 | "branch-alias": { 1449 | "dev-master": "1.3-dev" 1450 | } 1451 | }, 1452 | "autoload": { 1453 | "psr-4": { 1454 | "Webmozart\\Assert\\": "src/" 1455 | } 1456 | }, 1457 | "notification-url": "https://packagist.org/downloads/", 1458 | "license": [ 1459 | "MIT" 1460 | ], 1461 | "authors": [ 1462 | { 1463 | "name": "Bernhard Schussek", 1464 | "email": "bschussek@gmail.com" 1465 | } 1466 | ], 1467 | "description": "Assertions to validate method input/output with nice error messages.", 1468 | "keywords": [ 1469 | "assert", 1470 | "check", 1471 | "validate" 1472 | ], 1473 | "time": "2016-11-23T20:04:58+00:00" 1474 | } 1475 | ], 1476 | "aliases": [], 1477 | "minimum-stability": "stable", 1478 | "stability-flags": [], 1479 | "prefer-stable": false, 1480 | "prefer-lowest": false, 1481 | "platform": { 1482 | "php": ">=5.4.0" 1483 | }, 1484 | "platform-dev": [] 1485 | } 1486 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | ./tests 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/JsonBehavior.php: -------------------------------------------------------------------------------- 1 | function () { $this->initialization(); }, 35 | ActiveRecord::EVENT_AFTER_FIND => function () { $this->decode(); }, 36 | ActiveRecord::EVENT_BEFORE_INSERT => function () { $this->encode(); }, 37 | ActiveRecord::EVENT_BEFORE_UPDATE => function () { $this->encode(); }, 38 | ActiveRecord::EVENT_AFTER_INSERT => function () { $this->decode(); }, 39 | ActiveRecord::EVENT_AFTER_UPDATE => function () { $this->decode(); }, 40 | ActiveRecord::EVENT_BEFORE_VALIDATE => function () { 41 | if ($this->encodeBeforeValidation) { 42 | $this->encodeValidate(); 43 | } 44 | }, 45 | ActiveRecord::EVENT_AFTER_VALIDATE => function () { 46 | if ($this->encodeBeforeValidation) { 47 | $this->decode(); 48 | } 49 | }, 50 | ]; 51 | } 52 | 53 | /** 54 | */ 55 | protected function initialization() 56 | { 57 | foreach ($this->attributes as $attribute) { 58 | $this->owner->setAttribute($attribute, new JsonField()); 59 | } 60 | } 61 | 62 | /** 63 | */ 64 | protected function decode() 65 | { 66 | foreach ($this->attributes as $attribute) { 67 | $value = $this->owner->getAttribute($attribute); 68 | if (!$value instanceof JsonField) { 69 | $value = new JsonField($value); 70 | } 71 | $this->owner->setAttribute($attribute, $value); 72 | } 73 | } 74 | 75 | /** 76 | */ 77 | protected function encode() 78 | { 79 | foreach ($this->attributes as $attribute) { 80 | $field = $this->owner->getAttribute($attribute); 81 | if (!$field instanceof JsonField) { 82 | $field = new JsonField($field); 83 | } 84 | $this->owner->setAttribute($attribute, (string)$field ?: $this->emptyValue); 85 | } 86 | } 87 | 88 | /** 89 | */ 90 | protected function encodeValidate() 91 | { 92 | foreach ($this->attributes as $attribute) { 93 | $field = $this->owner->getAttribute($attribute); 94 | if ($field instanceof JsonField) { 95 | $this->owner->setAttribute($attribute, (string)$field ?: null); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/JsonField.php: -------------------------------------------------------------------------------- 1 | set($value); 23 | } 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function __toString() 29 | { 30 | return $this->value ? Json::encode($this->value) : ''; 31 | } 32 | 33 | /** 34 | * @param string|array $value 35 | */ 36 | public function set($value) 37 | { 38 | if ($value === null || $value === '') { 39 | $value = []; 40 | } elseif (is_string($value)) { 41 | $value = Json::decode($value, true); 42 | if (!is_array($value)) { 43 | throw new InvalidParamException('Value is scalar'); 44 | } 45 | } 46 | if (!is_array($value)) { 47 | throw new InvalidParamException('Value is not array'); 48 | } else { 49 | $this->value = $value; 50 | } 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function fields() 57 | { 58 | $fields = array_keys($this->value); 59 | return array_combine($fields, $fields); 60 | } 61 | 62 | /** 63 | * @inheritdoc 64 | */ 65 | public function extraFields() 66 | { 67 | return []; 68 | } 69 | 70 | /** 71 | * @inheritdoc 72 | */ 73 | public function toArray(array $fields = [], array $expand = [], $recursive = true) 74 | { 75 | return empty($fields) ? $this->value : array_intersect_key($this->value, array_flip($fields)); 76 | } 77 | 78 | /** 79 | * @return bool 80 | */ 81 | public function isEmpty() 82 | { 83 | return !$this->value; 84 | } 85 | 86 | /** 87 | * @inheritdoc 88 | */ 89 | #[\ReturnTypeWillChange] 90 | public function offsetExists($offset) 91 | { 92 | return isset($this->value[$offset]); 93 | } 94 | 95 | /** 96 | * @inheritdoc 97 | */ 98 | #[\ReturnTypeWillChange] 99 | public function &offsetGet($offset) 100 | { 101 | $null = null; 102 | if (isset($this->value[$offset])) { 103 | return $this->value[$offset]; 104 | } else { 105 | return $null; 106 | } 107 | } 108 | 109 | /** 110 | * @inheritdoc 111 | */ 112 | #[\ReturnTypeWillChange] 113 | public function offsetSet($offset, $value) 114 | { 115 | if ($offset === null) { 116 | $this->value[] = $value; 117 | } else { 118 | $this->value[$offset] = $value; 119 | } 120 | } 121 | 122 | /** 123 | * @inheritdoc 124 | */ 125 | #[\ReturnTypeWillChange] 126 | public function offsetUnset($offset) 127 | { 128 | unset($this->value[$offset]); 129 | } 130 | 131 | /** 132 | * @inheritDoc 133 | */ 134 | #[\ReturnTypeWillChange] 135 | public function getIterator() 136 | { 137 | return new ArrayIterator($this->value); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/JsonValidator.php: -------------------------------------------------------------------------------- 1 | $attribute; 28 | if (!$value instanceof JsonField) { 29 | try { 30 | $new = new JsonField($value); 31 | if ($this->merge) { 32 | /** @var BaseActiveRecord $model */ 33 | $old = new JsonField($model->getOldAttribute($attribute)); 34 | $new = new JsonField(array_merge($old->toArray(), $new->toArray())); 35 | } 36 | $model->$attribute = $new; 37 | } catch (InvalidParamException $e) { 38 | $this->addError($model, $attribute, $this->getErrorMessage($e)); 39 | $model->$attribute = new JsonField(); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * @param \Exception $exception 46 | * @return string 47 | */ 48 | protected function getErrorMessage($exception) 49 | { 50 | $code = $exception->getCode(); 51 | if (isset($this->errorMessages[$code])) { 52 | return $this->errorMessages[$code]; 53 | } 54 | return $exception->getMessage(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/BehaviorTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-json-behavior/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\jsonBehavior\tests; 9 | 10 | use tests\Item; 11 | use tests\ItemMerge; 12 | use tests\ItemRequired; 13 | use tests\TestMigration; 14 | use paulzi\jsonBehavior\JsonField; 15 | 16 | /** 17 | * @author PaulZi 18 | */ 19 | class BehaviorTest extends \PHPUnit_Framework_TestCase 20 | { 21 | public function testInitEmpty() 22 | { 23 | $model = new JsonField(); 24 | $this->assertSame((string)$model, ''); 25 | 26 | $model = new JsonField(''); 27 | $this->assertSame((string)$model, ''); 28 | 29 | $model = new JsonField(null); 30 | $this->assertSame((string)$model, ''); 31 | 32 | $model = new JsonField([]); 33 | $this->assertSame((string)$model, ''); 34 | } 35 | 36 | public function testInitString() 37 | { 38 | $model = new JsonField('{ "test": false }'); 39 | $this->assertSame((string)$model, '{"test":false}'); 40 | 41 | $model = new JsonField('{ "test": [1, 2, 3] }'); 42 | $this->assertSame((string)$model, '{"test":[1,2,3]}'); 43 | 44 | $model = new JsonField('{ "test": { "best": true}, "best": 2 }'); 45 | $this->assertSame((string)$model, '{"test":{"best":true},"best":2}'); 46 | 47 | $model = new JsonField('[1, false, "test", null]'); 48 | $this->assertSame((string)$model, '[1,false,"test",null]'); 49 | } 50 | 51 | /** 52 | * @expectedException \yii\base\InvalidParamException 53 | */ 54 | public function testInitStringParseException() 55 | { 56 | $model = new JsonField('{test:}'); 57 | } 58 | 59 | /** 60 | * @expectedException \yii\base\InvalidParamException 61 | */ 62 | public function testInitStringNumberScalarException() 63 | { 64 | $model = new JsonField('0'); 65 | } 66 | 67 | /** 68 | * @expectedException \yii\base\InvalidParamException 69 | */ 70 | public function testInitStringNullScalarException() 71 | { 72 | $model = new JsonField('null'); 73 | } 74 | 75 | /** 76 | * @expectedException \yii\base\InvalidParamException 77 | */ 78 | public function testInitStringFalseScalarException() 79 | { 80 | $model = new JsonField('false'); 81 | } 82 | 83 | /** 84 | * @expectedException \yii\base\InvalidParamException 85 | */ 86 | public function testInitStringTrueScalarException() 87 | { 88 | $model = new JsonField('true'); 89 | } 90 | 91 | public function testInitArray() 92 | { 93 | $model = new JsonField(['test' => false]); 94 | $this->assertSame((string)$model, '{"test":false}'); 95 | 96 | $model = new JsonField(['test' => [1, 2, 3]]); 97 | $this->assertSame((string)$model, '{"test":[1,2,3]}'); 98 | 99 | $model = new JsonField([1, false, "test", null]); 100 | $this->assertSame((string)$model, '[1,false,"test",null]'); 101 | } 102 | 103 | /** 104 | * @expectedException \yii\base\InvalidParamException 105 | */ 106 | public function testInitArrayException() 107 | { 108 | $model = new JsonField(new \stdClass()); 109 | } 110 | 111 | public function testSetString() 112 | { 113 | $model = new JsonField(); 114 | 115 | $model->set('{ "test": false }'); 116 | $this->assertSame((string)$model, '{"test":false}'); 117 | 118 | $model->set('{ "test": [1, 2, 3] }'); 119 | $this->assertSame((string)$model, '{"test":[1,2,3]}'); 120 | 121 | $model->set('{ "test": { "best": true}, "best": 2 }'); 122 | $this->assertSame((string)$model, '{"test":{"best":true},"best":2}'); 123 | 124 | $model->set('[1, false, "test", null]'); 125 | $this->assertSame((string)$model, '[1,false,"test",null]'); 126 | } 127 | 128 | /** 129 | * @expectedException \yii\base\InvalidParamException 130 | */ 131 | public function testSetStringParseException() 132 | { 133 | $model = new JsonField(); 134 | $model->set('{test:}'); 135 | } 136 | 137 | /** 138 | * @expectedException \yii\base\InvalidParamException 139 | */ 140 | public function testSetStringNumberScalarException() 141 | { 142 | $model = new JsonField(); 143 | $model->set('0'); 144 | } 145 | 146 | /** 147 | * @expectedException \yii\base\InvalidParamException 148 | */ 149 | public function testSetStringNullScalarException() 150 | { 151 | $model = new JsonField(); 152 | $model->set('null'); 153 | } 154 | 155 | /** 156 | * @expectedException \yii\base\InvalidParamException 157 | */ 158 | public function testSetStringFalseScalarException() 159 | { 160 | $model = new JsonField(); 161 | $model->set('false'); 162 | } 163 | 164 | /** 165 | * @expectedException \yii\base\InvalidParamException 166 | */ 167 | public function testSetStringTrueScalarException() 168 | { 169 | $model = new JsonField(); 170 | $model->set('true'); 171 | } 172 | 173 | public function testSetArray() 174 | { 175 | $model = new JsonField(); 176 | 177 | $model->set(['test' => false]); 178 | $this->assertSame((string)$model, '{"test":false}'); 179 | 180 | $model->set(['test' => [1, 2, 3]]); 181 | $this->assertSame((string)$model, '{"test":[1,2,3]}'); 182 | 183 | $model->set(['test' => ['best' => true], 'best' => 2]); 184 | $this->assertSame((string)$model, '{"test":{"best":true},"best":2}'); 185 | 186 | $model->set([1, false, "test", null]); 187 | $this->assertSame((string)$model, '[1,false,"test",null]'); 188 | } 189 | 190 | /** 191 | * @expectedException \yii\base\InvalidParamException 192 | */ 193 | public function testSetArrayException() 194 | { 195 | $model = new JsonField(new \stdClass()); 196 | } 197 | 198 | public function testArrayObjectAccess() 199 | { 200 | $model = new JsonField('{ "one": { "test": true }, "two": 2, "three": [1, 2, 3], "four": "4" }'); 201 | $this->assertSame($model['one'], ['test' => true]); 202 | $this->assertSame($model['one']['test'], true); 203 | $this->assertSame($model['two'], 2); 204 | $this->assertSame($model['three'][2], 3); 205 | $this->assertSame($model['four'], '4'); 206 | 207 | $model['one']['test'] = false; 208 | $model['two'] = 3; 209 | $model['three'][2] = 0; 210 | $model['four'] = null; 211 | $this->assertSame((string)$model, '{"one":{"test":false},"two":3,"three":[1,2,0],"four":null}'); 212 | } 213 | 214 | public function testArrayArrayAccess() 215 | { 216 | $model = new JsonField('[1, false, "test", null]'); 217 | $this->assertSame($model[0], 1); 218 | $this->assertSame($model[1], false); 219 | $this->assertSame($model[2], 'test'); 220 | $this->assertSame($model[3], null); 221 | 222 | $model[0] = 2; 223 | $model[1] = true; 224 | $model[2] = 'best'; 225 | $model[3] = ['test' => 'test']; 226 | $this->assertSame((string)$model, '[2,true,"best",{"test":"test"}]'); 227 | } 228 | 229 | public function testFields() 230 | { 231 | $model = new JsonField('{ "test": { "best": true}, "best": 2 }'); 232 | $this->assertSame($model->fields(), ['test' => 'test', 'best' => 'best']); 233 | } 234 | 235 | public function testToArray() 236 | { 237 | $model = new JsonField('{ "test": false }'); 238 | $this->assertSame($model->toArray(), ['test' => false]); 239 | 240 | $model = new JsonField('{ "test": [1, null, 3] }'); 241 | $this->assertSame($model->toArray(), ['test' => [1, null, 3]]); 242 | 243 | $model = new JsonField('{ "test": { "best": true}, "best": 2 }'); 244 | $this->assertSame($model->toArray(), ['test' => ['best' => true], 'best' => 2]); 245 | 246 | $model = new JsonField('[1, false, "test", null]'); 247 | $this->assertSame($model->toArray(), [1, false, "test", null]); 248 | 249 | $model = new JsonField('{ "test": { "best": true}, "best": 2 }'); 250 | $this->assertSame($model->toArray(['test']), ['test' => ['best' => true]]); 251 | } 252 | 253 | public function testIsEmpty() 254 | { 255 | $model = new JsonField(); 256 | $this->assertSame($model->isEmpty(), true); 257 | 258 | $model->set('{}'); 259 | $this->assertSame($model->isEmpty(), true); 260 | 261 | $model->set('[]'); 262 | $this->assertSame($model->isEmpty(), true); 263 | 264 | $model->set('[false]'); 265 | $this->assertSame($model->isEmpty(), false); 266 | 267 | $model->set('[0]'); 268 | $this->assertSame($model->isEmpty(), false); 269 | 270 | $model->set('[[]]'); 271 | $this->assertSame($model->isEmpty(), false); 272 | } 273 | 274 | public function testBehavior() 275 | { 276 | $item = new Item(); 277 | $item->params['one'] = 'value'; 278 | $item->params['two'] = []; 279 | $item->params['two']['test'] = true; 280 | $this->assertSame($item->toArray(), [ 281 | 'params' => [ 282 | 'one' => 'value', 283 | 'two' => ['test' => true], 284 | ], 285 | ]); 286 | $this->assertSame($item->params->toArray(['one']), ['one' => 'value']); 287 | $this->assertSame($item->save(false), true); 288 | $item->params['one'] = 42; 289 | $this->assertSame($item->params['one'], 42); 290 | 291 | $item = Item::findOne($item->id); 292 | $this->assertSame($item->params['one'], 'value'); 293 | $this->assertSame($item->params['two']['test'], true); 294 | } 295 | 296 | public function testRefresh() 297 | { 298 | $item = new Item(); 299 | $item->params['one'] = 3; 300 | $item->params['two'] = 2; 301 | $this->assertSame($item->save(false), true); 302 | $item->params['one'] = 'one'; 303 | $item->params['three'] = 3; 304 | $item->refresh(); 305 | $this->assertSame($item->params->toArray(), ['one' => 3, 'two' => 2]); 306 | } 307 | 308 | public function testDirtyAttributesOnUpdate() 309 | { 310 | $item = new Item(); 311 | $testChanged = null; 312 | $item->on($item::EVENT_AFTER_UPDATE, function ($event) use (&$testChanged) { 313 | $this->assertSame($event->changedAttributes, $testChanged); 314 | }); 315 | $item->params['one'] = 3; 316 | $item->params['two'] = 2; 317 | $this->assertSame($item->save(false), true); 318 | $testChanged = []; 319 | $this->assertSame($item->update(false), 0); 320 | $item->params['one'] = 1; 321 | $testChanged = ['params' => '{"one":3,"two":2}']; 322 | $this->assertSame($item->update(false), 1); 323 | } 324 | 325 | public function testEmptyValue() 326 | { 327 | $item = new Item(); 328 | $this->assertSame($item->save(false), true); 329 | $item->refresh(); 330 | $this->assertSame($item->getOldAttribute('params'), null); 331 | 332 | $item = new ItemMerge(); 333 | $this->assertSame($item->save(false), true); 334 | $item->refresh(); 335 | $this->assertSame($item->getOldAttribute('params'), '{}'); 336 | } 337 | 338 | public function testValidatorTest() 339 | { 340 | $item = new Item(); 341 | 342 | $item->attributes = ['params' => '{"json": true}']; 343 | $this->assertSame($item->validate(), true); 344 | 345 | $item->attributes = ['params' => ['json' => true]]; 346 | $this->assertSame($item->validate(), true); 347 | 348 | $item->attributes = ['params' => '{json:}']; 349 | $this->assertSame($item->validate(), false); 350 | $this->assertArrayHasKey('params', $item->errors); 351 | 352 | $item->attributes = ['params' => 'true']; 353 | $this->assertSame($item->validate(), false); 354 | $this->assertArrayHasKey('params', $item->errors); 355 | } 356 | 357 | public function testValidatorMergeTest() 358 | { 359 | $item = new ItemMerge(); 360 | $item->params['test1'] = 123; 361 | $item->params['test2'] = 456; 362 | $item->save(); 363 | 364 | $item->load(['params' => '{"test2": 789}'], ''); 365 | $this->assertSame($item->validate(), true); 366 | $this->assertSame($item->params->toArray(), ['test1' => 123, 'test2' => 789]); 367 | 368 | $item->load(['params' => ['test2' => 789]], ''); 369 | $this->assertSame($item->validate(), true); 370 | $this->assertSame($item->params->toArray(), ['test1' => 123, 'test2' => 789]); 371 | } 372 | 373 | public function testValidatorRequiredTest() 374 | { 375 | $item = new ItemRequired(); 376 | $item->params->set(null); 377 | $this->assertSame($item->validate(), false); 378 | 379 | $item = new ItemRequired(); 380 | $item->params->set('{}'); 381 | $this->assertSame($item->validate(), false); 382 | 383 | $item = new ItemRequired(); 384 | $item->params['test'] = 1; 385 | $this->assertSame($item->validate(), true); 386 | } 387 | 388 | public function testIterator() 389 | { 390 | $model = new JsonField('{"test":"best"}'); 391 | foreach ($model as $key => $value) { 392 | $this->assertSame($key, 'test'); 393 | $this->assertSame($value, 'best'); 394 | } 395 | } 396 | 397 | /** 398 | * @inheritdoc 399 | */ 400 | public static function setUpBeforeClass() 401 | { 402 | (new TestMigration())->up(); 403 | parent::setUpBeforeClass(); 404 | } 405 | } -------------------------------------------------------------------------------- /tests/Item.php: -------------------------------------------------------------------------------- 1 | JsonBehavior::className(), 26 | 'attributes' => ['params'], 27 | ] 28 | ]; 29 | } 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public function rules() 35 | { 36 | return [ 37 | [['params'], JsonValidator::className()], 38 | ]; 39 | } 40 | } -------------------------------------------------------------------------------- /tests/ItemMerge.php: -------------------------------------------------------------------------------- 1 | JsonBehavior::className(), 34 | 'attributes' => ['params'], 35 | 'emptyValue' => '{}', 36 | ] 37 | ]; 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function rules() 44 | { 45 | return [ 46 | [['params'], JsonValidator::className(), 'merge' => true], 47 | ]; 48 | } 49 | } -------------------------------------------------------------------------------- /tests/ItemRequired.php: -------------------------------------------------------------------------------- 1 | JsonBehavior::className(), 34 | 'attributes' => ['params'], 35 | ] 36 | ]; 37 | } 38 | 39 | /** 40 | * @inheritdoc 41 | */ 42 | public function rules() 43 | { 44 | return [ 45 | [['params'], 'required'], 46 | [['params'], JsonValidator::className()], 47 | ]; 48 | } 49 | } -------------------------------------------------------------------------------- /tests/TestMigration.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-json-behavior/blob/master/LICENSE) 6 | */ 7 | 8 | namespace tests; 9 | 10 | use yii\db\Schema; 11 | use yii\db\Migration; 12 | 13 | /** 14 | * @author PaulZi 15 | */ 16 | class TestMigration extends Migration 17 | { 18 | public function up() 19 | { 20 | ob_start(); 21 | if ($this->db->getTableSchema('{{%item}}', true) !== null) { 22 | $this->dropTable('{{%item}}'); 23 | } 24 | $this->createTable('{{%item}}', [ 25 | 'id' => $this->primaryKey(), 26 | 'params' => $this->text(), 27 | ]); 28 | 29 | // update cache (sqlite bug) 30 | $this->db->getSchema()->getTableSchema('{{%item}}', true); 31 | ob_end_clean(); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-adjacency-list/blob/master/LICENSE) 6 | */ 7 | 8 | defined('YII_DEBUG') or define('YII_DEBUG', true); 9 | defined('YII_ENV') or define('YII_ENV', 'test'); 10 | 11 | require(__DIR__ . '/../vendor/autoload.php'); 12 | require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); 13 | 14 | Yii::setAlias('@tests', __DIR__); 15 | 16 | new \yii\console\Application([ 17 | 'id' => 'unit', 18 | 'basePath' => __DIR__, 19 | 'components' => [ 20 | 'db' => [ 21 | 'class' => 'yii\db\Connection', 22 | 'dsn' => 'sqlite::memory:', 23 | ], 24 | ], 25 | ]); --------------------------------------------------------------------------------