├── .gitignore ├── composer.json ├── src ├── Support │ └── ShopifyFacade.php ├── Api │ ├── Traits │ │ └── OwnsMetafields.php │ ├── Shop.php │ ├── CustomCollection.php │ ├── Discount.php │ ├── Variants.php │ ├── Metafield.php │ ├── Product.php │ ├── Webhook.php │ ├── AbstractApi.php │ └── Order.php ├── Models │ ├── Traits │ │ ├── Taggable.php │ │ ├── Publishes.php │ │ └── OwnsMetafields.php │ ├── Webhook.php │ ├── CustomCollection.php │ ├── Discount.php │ ├── Metafield.php │ ├── Variant.php │ ├── Product.php │ ├── Shop.php │ ├── AbstractModel.php │ └── Order.php ├── Providers │ └── ShopifyServiceProvider.php ├── ClientTrait.php ├── Util.php ├── HttpClient.php ├── Client.php └── Manager.php ├── LICENSE.txt ├── readme.md └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | Thumbs.db 6 | /phpunit.xml 7 | /.idea 8 | /.vscode 9 | .phpunit.result.cache -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dan/shopify-api", 3 | "description": "Shopify API for PHP", 4 | "keywords": ["shopify", "api", "webhooks", "laravel"], 5 | "license": "MIT", 6 | "type": "project", 7 | "require": { 8 | "php": ">=5.5.9", 9 | "guzzle/guzzle": "~3.7" 10 | }, 11 | "require-dev": { 12 | "phpunit/phpunit": "~4.0" 13 | }, 14 | "autoload": { 15 | "psr-4": { 16 | "ShopifyApi\\": "src/" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Support/ShopifyFacade.php: -------------------------------------------------------------------------------- 1 | get("/{$api_wrap}/{$id}/metafields.json", $params); 23 | return is_array($arr) && isset($arr['metafields']) 24 | ? $arr['metafields'] : []; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Models/Traits/Taggable.php: -------------------------------------------------------------------------------- 1 | getOriginal('tags'))); 16 | } 17 | 18 | /** 19 | * @param array|string $tags 20 | * @return $this 21 | */ 22 | public function setTags($tags) 23 | { 24 | if (is_array($tags)) { 25 | $this->setOriginal('tags', array_filter(implode(',', $tags))); 26 | } elseif (is_string($tags)) { 27 | $this->setOriginal('tags', $tags); 28 | } 29 | return $this; 30 | } 31 | } -------------------------------------------------------------------------------- /src/Models/Webhook.php: -------------------------------------------------------------------------------- 1 | remove(); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/Providers/ShopifyServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('shopify-api', function ($app) { 25 | return Manager::init(env('SHOPIFY_DOMAIN'), env('SHOPIFY_TOKEN')); 26 | }); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/ClientTrait.php: -------------------------------------------------------------------------------- 1 | client; 20 | } 21 | 22 | /** 23 | * @return string 24 | */ 25 | public function getShop() 26 | { 27 | $shop = $this->getClient()->getHttpClient()->getOption('base_url'); 28 | $shop = preg_replace('/https?\:\/\//', '', $shop); 29 | return rtrim($shop, "/"); 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | public function getShopName() 36 | { 37 | return str_replace(".myshopify.com", '', $this->getShop()); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Dan Richards, https://github.com/danrichards/shopify-api 2 | Copyright (c) 2016 ShineOnCom 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/Models/CustomCollection.php: -------------------------------------------------------------------------------- 1 | setCollects($product_ids)->save(); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/Api/Shop.php: -------------------------------------------------------------------------------- 1 | get($this->getPath()); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/Models/Traits/Publishes.php: -------------------------------------------------------------------------------- 1 | getOriginal('published_at')) 26 | ? $date : new DateTime($date, $time_zone); 27 | } 28 | 29 | /** 30 | * @param DateTime|string 31 | * @return $this 32 | */ 33 | public function setPublishedAt($stringOrDateTime) 34 | { 35 | $this->data['published_at'] = $stringOrDateTime instanceof DateTime || $stringOrDateTime instanceof \Carbon\Carbon 36 | ? $stringOrDateTime->format('c') : $stringOrDateTime; 37 | 38 | return $this; 39 | } 40 | 41 | /** 42 | * @param bool $published 43 | * @return $this 44 | */ 45 | public function setPublished($published) 46 | { 47 | $this->data['published'] = $published; 48 | return $this; 49 | } 50 | 51 | /** 52 | * @return bool 53 | */ 54 | public function getPublished() 55 | { 56 | return ! empty($this->data['published_at']) 57 | && ! empty($this->data['published_scope']); 58 | } 59 | 60 | /** 61 | * Sugar for publishing 62 | * 63 | * @return $this 64 | */ 65 | public function publish() 66 | { 67 | if (get_class($this) == CustomCollection::class) { 68 | return $this->setPublished(true)->save(); 69 | } 70 | 71 | /** @var Product|CustomCollection $this */ 72 | return $this->setPublishedAt(new DateTime())->save(); 73 | } 74 | 75 | /** 76 | * Sugar for unpublishing 77 | * 78 | * @return $this 79 | */ 80 | public function unpublish() 81 | { 82 | if (get_class($this) == CustomCollection::class) { 83 | return $this->setPublished(false)->save(); 84 | } 85 | 86 | /** @var Product|CustomCollection $this */ 87 | return $this->setPublishedAt(null) 88 | ->save(); 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/Util.php: -------------------------------------------------------------------------------- 1 | getBody(true); 94 | 95 | $content = json_decode($body, true); 96 | 97 | if (JSON_ERROR_NONE !== json_last_error()) { 98 | return $body; 99 | } 100 | 101 | return $content; 102 | } 103 | 104 | /** 105 | * @return bool 106 | */ 107 | public static function isLaravel() 108 | { 109 | return defined('LARAVEL_START') && ! static::isLumen(); 110 | } 111 | 112 | /** 113 | * @return bool 114 | */ 115 | public static function isLumen() 116 | { 117 | return function_exists('app') 118 | && preg_match('/lumen/i', app()->version()); 119 | } 120 | } -------------------------------------------------------------------------------- /src/Models/Discount.php: -------------------------------------------------------------------------------- 1 | remove(); 62 | } 63 | 64 | /** 65 | * Disable Discount 66 | * 67 | * @return $this 68 | */ 69 | public function disable() 70 | { 71 | try { 72 | $this->id = $this->data['id']; 73 | if($this->data['status'] != 'disabled') { 74 | $this->data = $this->api->disable($this->data['id']); 75 | } 76 | } catch (BadMethodCallException $e) { 77 | throw new BadMethodCallException(sprintf( 78 | "You can't disable %s objects.", 79 | get_called_class() 80 | )); 81 | } 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Enable Discount 88 | * 89 | * @return $this 90 | */ 91 | public function enable() 92 | { 93 | try { 94 | $this->id = $this->data['id']; 95 | if($this->data['status'] != 'enabled') { 96 | $this->data = $this->api->enable($this->data['id']); 97 | } 98 | } catch (BadMethodCallException $e) { 99 | throw new BadMethodCallException(sprintf( 100 | "You can't disable %s objects.", 101 | get_called_class() 102 | )); 103 | } 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Update a Discount is not possible 110 | * 111 | * @return BadMethodCallException 112 | */ 113 | public function update() 114 | { 115 | throw new BadMethodCallException("Discount can't be updated. The can only by enabled, disabled and removed."); 116 | } 117 | } -------------------------------------------------------------------------------- /src/Api/CustomCollection.php: -------------------------------------------------------------------------------- 1 | get('/custom_collections.json', $params); 53 | } 54 | 55 | /** 56 | * Retrieve the number of products 57 | * 58 | * @link https://help.shopify.com/api/reference/custom_collections#count 59 | * 60 | * @param array $params 61 | * @return integer 62 | */ 63 | public function count(array $params = []) 64 | { 65 | $count = $this->get('/custom_collections/count.json', $params); 66 | return isset($count['count']) 67 | ? $count['count'] : 0; 68 | } 69 | 70 | /** 71 | * Find a CustomCollection 72 | * 73 | * @link https://help.shopify.com/api/reference/customcollections#show 74 | * 75 | * @param string $id the board's id 76 | * @param array $params optional attributes 77 | * @return array 78 | */ 79 | public function show($id, array $params = []) 80 | { 81 | return $this->get($this->getPath($id), $params); 82 | } 83 | 84 | /** 85 | * Create a CustomCollection 86 | * 87 | * @link https://help.shopify.com/api/reference/customcollections#create 88 | * 89 | * @param array $params Attributes 90 | * @return array 91 | */ 92 | public function create(array $params = array()) 93 | { 94 | $custom_collection = $params; 95 | 96 | return $this->post('/custom_collections.json', compact('custom_collection')); 97 | } 98 | 99 | /** 100 | * Update a CustomCollection 101 | * 102 | * @link https://help.shopify.com/api/reference/customcollections#update 103 | * 104 | * @param $id 105 | * @param array $params 106 | * @return array 107 | */ 108 | public function update($id, array $params = []) 109 | { 110 | return $this->put($this->getPath(rawurlencode($id)), $params); 111 | } 112 | 113 | /** 114 | * Delete a CustomCollection 115 | * 116 | * @link https://help.shopify.com/api/reference/customcollections#destroy 117 | * 118 | * @param $id 119 | * @return array 120 | */ 121 | public function remove($id) 122 | { 123 | return $this->delete($this->getPath(rawurlencode($id))); 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /src/Api/Discount.php: -------------------------------------------------------------------------------- 1 | discount_id = $discount_id; 54 | } 55 | 56 | /** 57 | * Create a Discount 58 | * 59 | * @link https://help.shopify.com/api/reference/discount#create 60 | * 61 | * @param array $discount optional attributes 62 | * @return array discount info 63 | */ 64 | public function create(array $discount = array()) 65 | { 66 | return $this->post("/discounts.json", compact('discount')); 67 | } 68 | 69 | /** 70 | * Retrieve all Discounts 71 | * 72 | * @link https://help.shopify.com/api/reference/discount#index 73 | * 74 | * @param array $params 75 | * limit: default=15, max = 200, 76 | * page: default=1 77 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 78 | */ 79 | public function all(array $params = []) 80 | { 81 | return $this->get('/discounts.json', $params); 82 | } 83 | 84 | /** 85 | * Find a discount 86 | * 87 | * @link https://help.shopify.com/api/reference/discount#show 88 | * 89 | * @param string $id the discount's id 90 | * @param array $params optional attributes 91 | * @return array discount 92 | */ 93 | public function show($id, array $params = []) 94 | { 95 | return $this->get("/discounts/{$id}.json", $params); 96 | } 97 | 98 | /** 99 | * Disable a discount 100 | * 101 | * @link https://help.shopify.com/api/reference/discount#disable 102 | * @param string $id the discount's id 103 | * 104 | * @return array discount 105 | */ 106 | public function disable($id) 107 | { 108 | return $this->post("/discounts/{$id}/disable.json"); 109 | } 110 | 111 | /** 112 | * Enable a discount 113 | * 114 | * @link https://help.shopify.com/api/reference/discount#enable 115 | * @param string $id the discount's id 116 | * 117 | * @return array discount 118 | */ 119 | public function enable($id) 120 | { 121 | return $this->post("/discounts/{$id}/enable.json"); 122 | } 123 | 124 | /** 125 | * Delete a discount (delete or remove do the same) 126 | * 127 | * @link https://help.shopify.com/api/reference/discount#delete 128 | * @param string $id the discount's id 129 | * 130 | * @return array empty 131 | */ 132 | public function remove($id) 133 | { 134 | return parent::delete("/discounts/{$id}.json"); 135 | } 136 | 137 | /** 138 | * Delete a discount (delete or remove do the same) 139 | * 140 | * @link https://help.shopify.com/api/reference/discount#delete 141 | * @param string $id the discount's id 142 | * 143 | * @param array $parameters 144 | * @param array $request_headers 145 | * @return array empty 146 | */ 147 | public function delete($id, array $parameters = [], $request_headers = []) 148 | { 149 | return parent::delete("/discounts/{$id}.json"); 150 | } 151 | 152 | /** 153 | * Update a Discount is not possible 154 | * 155 | * @param $id 156 | * @param array $params 157 | * @return BadMethodCallException 158 | */ 159 | public function update($id, array $params = []) 160 | { 161 | throw new BadMethodCallException("Discount can't be updated. The can only by enabled, disabled and removed."); 162 | } 163 | } -------------------------------------------------------------------------------- /src/Models/Metafield.php: -------------------------------------------------------------------------------- 1 | id = isset($data['id']) ? $data['id'] : null; 55 | $this->owner_resource = isset($data['owner_resource']) 56 | ? $data['owner_resource'] : null; 57 | $this->owner_id = isset($data['owner_id']) 58 | ? $data['owner_id'] : null; 59 | $this->data = $data; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * Update the object through API 66 | * 67 | * @return $this 68 | */ 69 | protected function update() 70 | { 71 | $this->preUpdate(); 72 | if ($this->owner_resource) { 73 | $api_owner = $this->owner_resource; 74 | $this->data = $this 75 | ->api 76 | ->$api_owner($this->owner_id) 77 | ->update($this->id, $this->data)[static::$api_name]; 78 | } else { 79 | $this->data = $this->api->update($this->id, $this->data)[static::$api_name]; 80 | } 81 | $this->postUpdate(); 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Create the object through API 88 | * 89 | * @return $this 90 | */ 91 | protected function create() 92 | { 93 | $this->preCreate(); 94 | if ($this->owner_resource) { 95 | $api_owner = $this->owner_resource; 96 | $this->data = $this 97 | ->api 98 | ->$api_owner($this->owner_id) 99 | ->create($this->data)[static::$api_name]; 100 | } else { 101 | $this->data = $this->api->create($this->data)[static::$api_name]; 102 | } 103 | $this->id = $this->data['id']; 104 | $this->postCreate(); 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Remove the object through API 111 | * 112 | * @return $this 113 | */ 114 | public function remove() 115 | { 116 | try { 117 | $this->preRemove(); 118 | $this->id = $this->data['id']; 119 | if ($this->owner_resource) { 120 | $api_owner = $this->owner_resource; 121 | $this->data = $this 122 | ->api 123 | ->$api_owner($this->owner_id) 124 | ->delete($this->data['id']); 125 | } else { 126 | $this->data = $this->api->delete($this->data['id']); 127 | } 128 | $this->postRemove(); 129 | } catch (BadMethodCallException $e) { 130 | throw new BadMethodCallException(sprintf( 131 | "You can't remove %s objects.", 132 | get_called_class() 133 | )); 134 | } 135 | 136 | return $this; 137 | } 138 | 139 | /** 140 | * @return null|Order|Product|Variant 141 | */ 142 | public function getResource() 143 | { 144 | switch ($this->getOwnerResource()) { 145 | case 'product': 146 | return new Product($this->client, $this->getOwnerId()); 147 | case 'variant': 148 | return new Variant($this->client, $this->getOwnerId()); 149 | case 'order': 150 | return new Order($this->client, $this->getOwnerId()); 151 | default: 152 | return null; 153 | } 154 | } 155 | 156 | } -------------------------------------------------------------------------------- /src/Models/Variant.php: -------------------------------------------------------------------------------- 1 | product_id = $product_id; 96 | } 97 | 98 | $this->client = $client; 99 | 100 | if (is_array($id_or_data)) { 101 | $id =isset($id_or_data['id']) ? $id_or_data['id'] : null; 102 | $this->api = $client->api(static::$api_name, $id); 103 | $this->fields = $this->api->product($product_id)->getFields(); 104 | $this->setData($id_or_data); 105 | } else { 106 | $this->api = $client->api(static::$api_name, $id_or_data); 107 | $this->fields = $this->api->product($product_id)->getFields(); 108 | if ($id_or_data) { 109 | $this->id = $id_or_data; 110 | $this->refresh(); 111 | } 112 | } 113 | } 114 | 115 | /** 116 | * @return $this 117 | */ 118 | public function refresh() 119 | { 120 | $this->preRefresh(); 121 | $this->data = $this->api->show($this->id, static::$load_params); 122 | if (is_array($this->data) && array_key_exists(static::$api_name, $this->data)) { 123 | $this->setData($this->data[static::$api_name]); 124 | } 125 | $this->postRefresh(); 126 | 127 | return $this; 128 | } 129 | 130 | /** 131 | * When you set the title, option1 must match it. 132 | * 133 | * @param $title 134 | * @return $this 135 | */ 136 | public function setTitle($title) 137 | { 138 | $this->setOption1($title); 139 | return $this; 140 | } 141 | 142 | /** 143 | * @return \ShopifyApi\Models\Product 144 | */ 145 | public function product() 146 | { 147 | return new Product($this->client, $this->getProductId()); 148 | } 149 | 150 | } -------------------------------------------------------------------------------- /src/Models/Traits/OwnsMetafields.php: -------------------------------------------------------------------------------- 1 | client->api('metafields')->$api($this->getId())->all(); 25 | 26 | if (! empty($metafields) && isset($metafields['metafields'])) { 27 | return array_map(function($metafield) { 28 | return new Metafield($this->client, $metafield); 29 | }, $metafields['metafields']); 30 | } 31 | 32 | return []; 33 | } 34 | 35 | /** 36 | * @return array 37 | */ 38 | public function getMetafields() 39 | { 40 | return $this->metafields(); 41 | } 42 | 43 | /** 44 | * @param string $key 45 | * @param string|null $namespace 46 | * @return Metafield|null 47 | */ 48 | public function getMetafield($key, $namespace) { 49 | foreach ($this->metafields() as $m) { 50 | if ($m->getKey() == $key && $m->getNamespace() == $namespace) { 51 | return $m; 52 | } 53 | } 54 | 55 | return null; 56 | } 57 | 58 | /** 59 | * @param string $key 60 | * @param string|null $namespace 61 | * @return bool 62 | */ 63 | public function hasMetafield($key, $namespace) { 64 | return ! empty($this->getMetafield($key, $namespace)); 65 | } 66 | 67 | /** 68 | * @param $key 69 | * @param $namespace 70 | * @param array $attributes 71 | * @return Metafield 72 | */ 73 | public function createMetafield($key, $namespace, array $attributes = []) 74 | { 75 | $api = static::$api_name; 76 | 77 | // Allow for no value to be set 78 | if (! isset($attributes['value'])) { 79 | $attributes['value'] = json_encode(null); 80 | $attributes['value_type'] = 'string'; 81 | } 82 | 83 | // Allow for arrays and objects to be json_encoded 84 | if (! is_int($attributes['value']) && ! is_string($attributes['value'])) { 85 | $attributes['value'] = (string) @json_encode($attributes['value']); 86 | $attributes['value_type'] = 'string'; 87 | } 88 | 89 | // Determine the value_type if it's not already specified 90 | if (! isset($attributes['value_type'])) { 91 | $attributes['value_type'] = is_int($attributes['value']) 92 | ? 'integer' : 'string'; 93 | } 94 | 95 | $arr = $this->client 96 | ->api('metafields') 97 | ->$api($this->getId()) 98 | ->create(array_merge(compact('key', 'namespace'), $attributes)); 99 | 100 | return new Metafield($this->client, $arr); 101 | } 102 | 103 | /** 104 | * @param string $key 105 | * @param string $namespace 106 | * @param array $attributes 107 | * @return Metafield 108 | */ 109 | public function updateMetafield($key, $namespace, array $attributes) 110 | { 111 | $omit = ['id', 'owner_id', 'owner_resource', 'created_at', 'updated_at']; 112 | 113 | if (empty($updated = $this->getMetafield($key, $namespace))) { 114 | throw new BadMethodCallException('Metafield does not exist.'); 115 | } 116 | 117 | foreach (array_diff(MetafieldApi::$fields, $omit) as $field) { 118 | if (isset($attributes[$field])) { 119 | $updated->setOriginal($field, $attributes[$field]); 120 | } 121 | } 122 | 123 | if (isset($attributes['updated_at'])) { 124 | $updated->setUpdatedAt($attributes['updated_at']); 125 | } 126 | 127 | $updated->save(); 128 | 129 | return $updated; 130 | } 131 | 132 | /** 133 | * @param string $key 134 | * @param string $namespace 135 | * 136 | * @return boolean | BadMethodCallException 137 | */ 138 | public function deleteMetafield($key, $namespace) 139 | { 140 | if (empty($updated = $this->getMetafield($key, $namespace))) { 141 | throw new BadMethodCallException('Metafield does not exist.'); 142 | } 143 | 144 | $updated->remove(); 145 | return true; 146 | } 147 | /** 148 | * @param $key 149 | * @param $namespace 150 | * @param array $attributes 151 | * @return Metafield 152 | */ 153 | public function updateOrCreateMetafield($key, $namespace, array $attributes = []) 154 | { 155 | try { 156 | return $this->updateMetafield($key, $namespace, $attributes); 157 | } catch (BadMethodCallException $e) { 158 | return $this->createMetafield($key, $namespace, $attributes); 159 | } 160 | } 161 | 162 | /** 163 | * @param $key 164 | * @param $namespace 165 | * @param array $attributes 166 | * @return Metafield | bool | BadMethodCallException 167 | */ 168 | public function updateCreateOrRemoveMetafield($key, $namespace, array $attributes = []) 169 | { 170 | if(!isset($attributes['value']) || empty($attributes['value'])){ 171 | if(empty($this->getMetafield($key, $namespace))){ 172 | return false; 173 | } 174 | return $this->deleteMetafield($key, $namespace); 175 | } 176 | 177 | return $this->updateOrCreateMetafield($key, $namespace, $attributes); 178 | } 179 | } -------------------------------------------------------------------------------- /src/Models/Product.php: -------------------------------------------------------------------------------- 1 | getVariants(); 67 | 68 | foreach($all_variants as $variant_data) { 69 | if ($variant_data['id'] == $variant_id) { 70 | return new Variant($this->client, $variant_data); 71 | } 72 | } 73 | 74 | // fail soft 75 | return null; 76 | } 77 | 78 | /** 79 | * Variants API 80 | * 81 | * @return array [\ShopifyApi\Models\Variant] 82 | */ 83 | public function variants() 84 | { 85 | $variants = $this->getVariants(); 86 | return array_map(function($variant) { 87 | return new Variant($this->client, $variant); 88 | }, $variants); 89 | } 90 | 91 | // ------------------------------------------------------------------------ 92 | // SUPPORT FOR PRODUCT IMAGES 93 | // ------------------------------------------------------------------------ 94 | 95 | /** 96 | * Retrieve all risks for an order 97 | * 98 | * @link https://help.shopify.com/api/reference/product_image#index 99 | * 100 | * @param array $params 101 | * @return array 102 | */ 103 | public function images(array $params = []) 104 | { 105 | $response = $this->api->images($this->getId(), $params); 106 | return isset($response['images']) 107 | ? $response['images'] 108 | : $response; 109 | } 110 | 111 | /** 112 | * @link https://help.shopify.com/api/reference/product_image#create 113 | * 114 | * @param array $params 115 | * @return array 116 | */ 117 | public function createImage(array $params = []) 118 | { 119 | $response = $this->api->createImage($this->getId(), $params); 120 | return isset($response['image']) 121 | ? $response['image'] 122 | : $response; 123 | } 124 | 125 | /** 126 | * @link https://help.shopify.com/api/reference/product_image#update 127 | * 128 | * @param $image_id 129 | * @param array $params 130 | * @return array 131 | */ 132 | public function updateImage($image_id, array $params = []) 133 | { 134 | $response = $this->api->updateImage($this->getId(), $image_id, $params); 135 | return isset($response['image']) 136 | ? $response['image'] 137 | : $response; 138 | } 139 | 140 | /** 141 | * @link https://help.shopify.com/api/reference/product_image#show 142 | * 143 | * @param $image_id 144 | * @param array $params 145 | * @return array 146 | */ 147 | public function showImage($image_id, array $params = []) 148 | { 149 | $response = $this->api->showImage($this->getId(), $image_id, $params); 150 | return isset($response['image']) 151 | ? $response['image'] 152 | : $response; 153 | } 154 | 155 | /** 156 | * @link https://help.shopify.com/api/reference/product_image#count 157 | * 158 | * @param array $params 159 | * @return integer 160 | */ 161 | public function countImages(array $params = []) 162 | { 163 | $response = $this->api->countImages($this->getId(), $params); 164 | return isset($response['count']) 165 | ? $response['count'] 166 | : $response; 167 | } 168 | 169 | /** 170 | * @link https://help.shopify.com/api/reference/product_image#destroy 171 | * 172 | * @param $image_id 173 | * @return array 174 | */ 175 | public function deleteImage($image_id) 176 | { 177 | $response = $this->api->deleteImage($this->getId(), $image_id); 178 | return isset($response['image']) 179 | ? $response['image'] 180 | : $response; 181 | } 182 | 183 | } -------------------------------------------------------------------------------- /src/Api/Variants.php: -------------------------------------------------------------------------------- 1 | variant_id = $variant_id; 74 | $this->product_id = $product_id; 75 | } 76 | 77 | /** 78 | * @param $product_id 79 | * @return $this 80 | */ 81 | public function product($product_id) 82 | { 83 | $this->product_id = $product_id; 84 | return $this; 85 | } 86 | 87 | /** 88 | * Retrieve all Product Variants (api limit is 250) 89 | * 90 | * @link https://help.shopify.com/api/reference/product_variant#index 91 | * 92 | * @param array $params 93 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 94 | */ 95 | public function all(array $params = []) 96 | { 97 | $product_id = $this->product_id; 98 | 99 | if (empty($product_id)) { 100 | throw new BadMethodCallException('Please specify a product id.'); 101 | } 102 | 103 | return $this->get($this->getRelatedPath($product_id), $params); 104 | } 105 | 106 | /** 107 | * Retrieve the number of variants 108 | * 109 | * @link https://help.shopify.com/api/reference/product_variant#count 110 | * 111 | * @param array $params 112 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 113 | */ 114 | public function count(array $params = []) 115 | { 116 | if (empty($this->product_id)) { 117 | throw new BadMethodCallException('Please specify a product id.'); 118 | } 119 | 120 | $count = $this->get(sprintf('/products/%d/variants/count.json', $this->product_id), $params); 121 | 122 | return isset($count['count']) ? $count['count'] : 0; 123 | } 124 | 125 | /** 126 | * Find a Product Variant 127 | * 128 | * @link https://help.shopify.com/api/reference/product_variant#show 129 | * 130 | * @param string $id the board's id 131 | * @param array $params optional attributes 132 | * 133 | * @return array board info 134 | */ 135 | public function show($id, array $params = []) 136 | { 137 | return $this->get($this->getPath($id), $params); 138 | } 139 | 140 | /** 141 | * Update a Product Variant 142 | * 143 | * @link https://help.shopify.com/api/reference/product_variant#update 144 | * 145 | * @param $id 146 | * @param array $params 147 | * @return array 148 | */ 149 | public function update($id, array $params = []) 150 | { 151 | return $this->put($this->getPath(rawurlencode($id)), $params); 152 | } 153 | 154 | /** 155 | * Delete a Product Variant 156 | * 157 | * @link https://help.shopify.com/api/reference/product_variant#delete 158 | * 159 | * @param $product_id 160 | * @param $id 161 | * @param array $params 162 | * @return array 163 | */ 164 | public function remove($product_id, $id, array $params = []) 165 | { 166 | $path = sprintf('/products/%d/variants/%d.json', $product_id, $id); 167 | return $this->delete($path, $params); 168 | } 169 | 170 | /** 171 | * Create a Product Variant 172 | * 173 | * @link https://help.shopify.com/api/reference/product_variant#create 174 | * 175 | * @param array $params optional attributes 176 | * 177 | * @return array card info 178 | */ 179 | public function create(array $params = array()) 180 | { 181 | $product_id = isset($params['product_id']) 182 | ? $params['product_id'] 183 | : $this->product_id; 184 | 185 | if (empty($product_id)) { 186 | throw new BadMethodCallException('Please specify a product id.'); 187 | } 188 | 189 | $option_required = array_fill_keys(['option1', 'option2', 'option2'], null); 190 | 191 | if (empty(array_intersect_key($params, $option_required))) { 192 | throw new BadMethodCallException('Please specify a option.'); 193 | } 194 | 195 | $variant = $params; 196 | 197 | return $this->post($this->getRelatedPath($product_id), compact('variant')); 198 | } 199 | 200 | /** 201 | * @param $product_id 202 | * @return string 203 | */ 204 | private function getRelatedPath($product_id) 205 | { 206 | return sprintf('/products/%d/variants.json', $product_id); 207 | } 208 | 209 | } -------------------------------------------------------------------------------- /src/HttpClient.php: -------------------------------------------------------------------------------- 1 | 'php-shopify-api (http://github.com/ShineOnCom/php-shopify-api)', 22 | 'timeout' => 10 23 | ); 24 | 25 | /** @var GuzzleClient $client */ 26 | protected $client; 27 | 28 | /** @var array $headers */ 29 | protected $headers = []; 30 | 31 | /** @var Response $lastResponse */ 32 | private $lastResponse; 33 | 34 | /** @var Request $lastRequest */ 35 | private $lastRequest; 36 | 37 | /** 38 | * @param array $options 39 | * @param ClientInterface $client 40 | */ 41 | public function __construct(array $options = array(), $client = null) 42 | { 43 | $this->options = array_merge($this->options, $options); 44 | $client = $client ?: new GuzzleClient($this->options['base_url'], $this->options); 45 | $this->client = $client; 46 | } 47 | 48 | /** 49 | * @return array 50 | */ 51 | public function getOptions() 52 | { 53 | return $this->options; 54 | } 55 | 56 | /** 57 | * Get option by name 58 | * 59 | * @param string $name the option's name 60 | * 61 | * @return mixed 62 | * 63 | * @throws InvalidArgumentException 64 | */ 65 | public function getOption($name) 66 | { 67 | if (! array_key_exists($name, $this->options)) { 68 | throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); 69 | } 70 | 71 | return $this->options[$name]; 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public function setOption($name, $value) 78 | { 79 | $this->options[$name] = $value; 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | public function get($path, array $parameters = array(), array $headers = array()) 86 | { 87 | return $this->request($path, $parameters, 'GET', $headers); 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | public function post($path, $body = null, array $headers = array()) 94 | { 95 | return $this->request($path, $body, 'POST', $headers); 96 | } 97 | 98 | /** 99 | * {@inheritDoc} 100 | */ 101 | public function patch($path, $body = null, array $headers = array()) 102 | { 103 | return $this->request($path, $body, 'PATCH', $headers); 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | public function delete($path, $body = null, array $headers = array()) 110 | { 111 | return $this->request($path, $body, 'DELETE', $headers); 112 | } 113 | 114 | /** 115 | * {@inheritDoc} 116 | */ 117 | public function put($path, $body, array $headers = array()) 118 | { 119 | return $this->request($path, $body, 'PUT', $headers); 120 | } 121 | 122 | /** 123 | * {@inheritDoc} 124 | */ 125 | public function request($path, $body = null, $httpMethod = 'GET', array $headers = array(), array $options = array()) 126 | { 127 | $request = $this->createRequest($httpMethod, ltrim($path, '/'), $body, $headers, $options); 128 | 129 | $this->lastRequest = $request; 130 | 131 | try { 132 | $response = $this->client->send($request); 133 | } catch (ClientErrorResponseException $e) { 134 | $responseBody = $e->getResponse()->getBody(true); 135 | throw new RuntimeException( 136 | sprintf('%s\n[body] %s', $e->getMessage(),$responseBody), $e->getCode(), $e 137 | ); 138 | } catch (\LogicException $e) { 139 | throw new ErrorException($e->getMessage(), $e->getCode(), $e); 140 | } catch (\RuntimeException $e) { 141 | throw new RuntimeException($e->getMessage(), $e->getCode(), $e); 142 | } 143 | 144 | $this->lastResponse = $response; 145 | 146 | $api_deprecated_reason = $response->getHeader('X-Shopify-API-Deprecated-Reason'); 147 | $api_version_warning = $response->getHeader('X-Shopify-Api-Version-Warning'); 148 | if (($api_deprecated_reason || $api_version_warning) && function_exists('logger')) { 149 | $api_version = $response->getHeader('X-Shopify-Api-Version'); 150 | logger('vendor:dan:shopify-api:deprecated', 151 | compact('api_version', 'api_version_warning', 'api_deprecated_reason') + 152 | ['request' => compact('httpMethod', 'path', 'body', 'options')]); 153 | } 154 | 155 | return $response; 156 | } 157 | 158 | /** 159 | * @return Request 160 | */ 161 | public function getLastRequest() 162 | { 163 | return $this->lastRequest; 164 | } 165 | 166 | /** 167 | * @return Response 168 | */ 169 | public function getLastResponse() 170 | { 171 | return $this->lastResponse; 172 | } 173 | 174 | /** 175 | * Make a request with Guzzle 176 | * 177 | * @param string $httpMethod 178 | * @param string $path 179 | * @param null $body 180 | * @param array $headers 181 | * @param array $options 182 | * @return \Guzzle\Http\Message\RequestInterface 183 | */ 184 | protected function createRequest($httpMethod, $path, $body = null, array $headers = array(), array $options = array()) 185 | { 186 | if ($httpMethod === 'GET' && $body) { 187 | $path .= (false === strpos($path, '?') ? '?' : '&'); 188 | $path .= utf8_encode(http_build_query($body, '', '&')); 189 | } 190 | 191 | return $this->client->createRequest( 192 | $httpMethod, 193 | $path, 194 | array_merge($this->headers, $headers), 195 | $body, 196 | $options 197 | ); 198 | } 199 | 200 | /** 201 | * @return GuzzleClient 202 | */ 203 | public function getClient() 204 | { 205 | return $this->client; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/Api/Metafield.php: -------------------------------------------------------------------------------- 1 | metafield_id = $metafield_id; 56 | } 57 | 58 | /** 59 | * Retrieve all Metafields (api limit is 250) 60 | * 61 | * @link https://help.shopify.com/api/reference/metafield#index 62 | * 63 | * @param array $params 64 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 65 | */ 66 | public function all(array $params = []) 67 | { 68 | $url = '/metafields.json'; 69 | 70 | if ($this->fluent_resource) { 71 | $url = "/{$this->fluent_resource}/{$this->fluent_resource_id}/metafields.json"; 72 | $this->fluent_resource = $this->fluent_resource_id = null; 73 | } 74 | 75 | return $this->get($url, $params); 76 | } 77 | 78 | /** 79 | * Retrieve the number of variants 80 | * 81 | * @link https://help.shopify.com/api/reference/metafield#count 82 | * 83 | * @param array $params 84 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 85 | */ 86 | public function count(array $params = []) 87 | { 88 | $url = '/metafields.json'; 89 | 90 | if ($this->fluent_resource) { 91 | $url = "/{$this->fluent_resource}/{$this->fluent_resource_id}/metafields/count.json"; 92 | $this->fluent_resource = $this->fluent_resource_id = null; 93 | } 94 | 95 | $count = $this->get($url, $params); 96 | 97 | return isset($count['count']) ? $count['count'] : 0; 98 | } 99 | 100 | /** 101 | * Find a metafield 102 | * 103 | * @link https://help.shopify.com/api/reference/metafield#show 104 | * 105 | * @param string $id the board's id 106 | * @param array $params optional attributes 107 | * 108 | * @return array board info 109 | */ 110 | public function show($id, array $params = []) 111 | { 112 | $url = "/metafields/{$id}.json"; 113 | 114 | if ($this->fluent_resource) { 115 | $url = "/{$this->fluent_resource}/{$this->fluent_resource_id}/metafields/{$id}.json"; 116 | $this->fluent_resource = $this->fluent_resource_id = null; 117 | } 118 | 119 | return $this->get($url, $params); 120 | } 121 | 122 | /** 123 | * Update a Metafield 124 | * 125 | * @link https://help.shopify.com/api/reference/metafield#update 126 | * 127 | * @param $id 128 | * @param array $params 129 | * @return array 130 | */ 131 | public function update($id, array $params = []) 132 | { 133 | $url = "/metafields/{$id}.json"; 134 | 135 | if ($this->fluent_resource) { 136 | $url = "/{$this->fluent_resource}/{$this->fluent_resource_id}/metafields/{$id}.json"; 137 | $this->fluent_resource = $this->fluent_resource_id = null; 138 | } 139 | 140 | return $this->put($url, $params); 141 | } 142 | 143 | /** 144 | * Delete a Metafield 145 | * 146 | * @link https://help.shopify.com/api/reference/metafield#destroy 147 | * 148 | * @param string $id 149 | * @param array $params 150 | * @param array $request_headers 151 | * @return array 152 | */ 153 | public function delete($id, array $params = [], $request_headers = []) 154 | { 155 | $url = "/metafields/{$id}.json"; 156 | 157 | if ($this->fluent_resource) { 158 | $url = "/{$this->fluent_resource}/{$this->fluent_resource_id}/metafields/{$id}.json"; 159 | $this->fluent_resource = $this->fluent_resource_id = null; 160 | } 161 | 162 | return parent::delete($url, $params); 163 | } 164 | 165 | /** 166 | * Create a Metafield 167 | * 168 | * @link https://help.shopify.com/api/reference/metafield#create 169 | * 170 | * @param array $params optional attributes 171 | * 172 | * @return array card info 173 | */ 174 | public function create(array $params = array()) 175 | { 176 | $url = "/metafields.json"; 177 | 178 | if ($this->fluent_resource) { 179 | $url = "/{$this->fluent_resource}/{$this->fluent_resource_id}/metafields.json"; 180 | $this->fluent_resource = $this->fluent_resource_id = null; 181 | } 182 | 183 | $metafield = $params; 184 | 185 | return $this->post($url, compact('metafield')); 186 | } 187 | 188 | /** 189 | * @param $product_id 190 | * @return $this 191 | */ 192 | public function product($product_id) 193 | { 194 | $this->fluent_resource = 'products'; 195 | $this->fluent_resource_id = $product_id; 196 | return $this; 197 | } 198 | 199 | /** 200 | * @param $variant_id 201 | * @return $this 202 | */ 203 | public function variant($variant_id) 204 | { 205 | $this->fluent_resource = 'variants'; 206 | $this->fluent_resource_id = $variant_id; 207 | return $this; 208 | } 209 | 210 | /** 211 | * @param $order_id 212 | * @return $this 213 | */ 214 | public function order($order_id) 215 | { 216 | $this->fluent_resource = 'orders'; 217 | $this->fluent_resource_id = $order_id; 218 | return $this; 219 | } 220 | 221 | } -------------------------------------------------------------------------------- /src/Api/Product.php: -------------------------------------------------------------------------------- 1 | get('/products.json', $params); 61 | } 62 | 63 | /** 64 | * Retrieve the number of products 65 | * 66 | * @link https://help.shopify.com/api/reference/product#count 67 | * 68 | * @param array $params 69 | * @return integer 70 | */ 71 | public function count(array $params = []) 72 | { 73 | $count = $this->get('/products/count.json', $params); 74 | return isset($count['count']) 75 | ? $count['count'] : 0; 76 | } 77 | 78 | /** 79 | * Find a Product 80 | * 81 | * @link https://help.shopify.com/api/reference/product#show 82 | * 83 | * @param string $id the board's id 84 | * @param array $params optional attributes 85 | * @return array 86 | */ 87 | public function show($id, array $params = []) 88 | { 89 | return $this->get($this->getPath($id), $params); 90 | } 91 | 92 | /** 93 | * Create a Product 94 | * 95 | * @link https://help.shopify.com/api/reference/product#create 96 | * 97 | * @param array $params Attributes 98 | * @return array 99 | */ 100 | public function create(array $params = array()) 101 | { 102 | $product = $params; 103 | 104 | return $this->post('/products.json', compact('product')); 105 | } 106 | 107 | /** 108 | * Update a Product 109 | * 110 | * @link https://help.shopify.com/api/reference/product#update 111 | * 112 | * @param $id 113 | * @param array $params 114 | * @return array 115 | */ 116 | public function update($id, array $params = []) 117 | { 118 | return $this->put($this->getPath(rawurlencode($id)), $params); 119 | } 120 | 121 | /** 122 | * Delete a Product 123 | * 124 | * @link https://help.shopify.com/api/reference/product#destroy 125 | * 126 | * @param $id 127 | * @return array 128 | */ 129 | public function remove($id) 130 | { 131 | return $this->delete($this->getPath(rawurlencode($id))); 132 | } 133 | 134 | /** 135 | * Variants API 136 | * 137 | * @return array [Variant] 138 | */ 139 | public function variants() 140 | { 141 | $variants = $this->getVariants(); 142 | return array_map(function($variant) { 143 | return new Variant($this->client, $variant); 144 | }, $variants); 145 | } 146 | 147 | // ------------------------------------------------------------------------ 148 | // SUPPORT FOR PRODUCT IMAGES 149 | // ------------------------------------------------------------------------ 150 | 151 | /** 152 | * Retrieve all images for a product 153 | * 154 | * @link https://help.shopify.com/api/reference/product_image#index 155 | * 156 | * @param $id 157 | * @param array $params 158 | * @return array 159 | */ 160 | public function images($id, array $params = []) 161 | { 162 | $alt_path = '/products/#id#/images.json'; 163 | return $this->get($this->getPath($id, $alt_path), $params); 164 | } 165 | 166 | /** 167 | * @link https://help.shopify.com/api/reference/product_image#create 168 | * 169 | * @param $id 170 | * @param array $params 171 | * @return array 172 | */ 173 | public function createImage($id, array $params = []) 174 | { 175 | $alt_path = '/products/#id#/images.json'; 176 | $image = $params; 177 | return $this->post($this->getPath($id, $alt_path), compact('image')); 178 | } 179 | 180 | /** 181 | * @link https://help.shopify.com/api/reference/product_image#update 182 | * 183 | * @param $id 184 | * @param $image_id 185 | * @param array $params 186 | * @return array 187 | */ 188 | public function updateImage($id, $image_id, array $params = []) 189 | { 190 | $alt_path = "/products/#id#/images/{$image_id}.json"; 191 | $image = $params; 192 | return $this->post($this->getPath($id, $alt_path), compact('image')); 193 | } 194 | 195 | /** 196 | * @link https://help.shopify.com/api/reference/product_image#show 197 | * 198 | * @param $id 199 | * @param $image_id 200 | * @return array 201 | */ 202 | public function showImage($id, $image_id) 203 | { 204 | $alt_path = "/products/#id#/images/{$image_id}.json"; 205 | return $this->get($this->getPath($id, $alt_path)); 206 | } 207 | 208 | /** 209 | * @link https://help.shopify.com/api/reference/product_image#count 210 | * 211 | * @param $id 212 | * @param array $params 213 | * @return array 214 | */ 215 | public function countImages($id, array $params = []) 216 | { 217 | $alt_path = "/products/#id#/images/count.json"; 218 | return $this->get($this->getPath($id, $alt_path), $params); 219 | } 220 | 221 | /** 222 | * @link https://help.shopify.com/api/reference/product_image#destroy 223 | * 224 | * @param $id 225 | * @param $image_id 226 | * @return array 227 | */ 228 | public function deleteImage($id, $image_id) 229 | { 230 | $alt_path = "/products/#id#/images/{$image_id}.json"; 231 | return $this->delete($this->getPath($id, $alt_path)); 232 | } 233 | 234 | } -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 'php-shopify-api (http://github.com/ShineOnCom/php-shopify-api)', 65 | 'timeout' => 10, 66 | 'api_limit' => 250, 67 | 'api_version' => 1, 68 | 'cache_dir' => null, 69 | ); 70 | 71 | /** 72 | * The Buzz instance used to communicate with Trello 73 | * 74 | * @var HttpClient 75 | */ 76 | private $httpClient; 77 | 78 | /** 79 | * Instantiate a new Trello client 80 | * 81 | * @param null|HttpClient $httpClient Shopify http client 82 | */ 83 | public function __construct($httpClient = null) 84 | { 85 | $this->httpClient = $httpClient; 86 | } 87 | 88 | /** 89 | * Proxies $this->members() to $this->api('members') 90 | * 91 | * @param string $name method name 92 | * @param array $args arguments 93 | * 94 | * @return AbstractApi 95 | * 96 | * @throws BadMethodCallException 97 | */ 98 | public function __call($name, $args) 99 | { 100 | try { 101 | return $this->api($name); 102 | } catch (InvalidArgumentException $e) { 103 | throw new BadMethodCallException(sprintf('Undefined method called: "%s"', $name)); 104 | } 105 | } 106 | 107 | /** 108 | * Get an API by name 109 | * 110 | * @param string $name 111 | * @return AbstractApi 112 | * 113 | * @throws InvalidArgumentException if the requested api does not exist 114 | */ 115 | public function api($name) 116 | { 117 | switch ($name) { 118 | case 'custom_collection': 119 | case 'custom_collections': 120 | $api = new CustomCollection($this); 121 | break; 122 | case 'discount': 123 | case 'discounts': 124 | $api = new Discount($this); 125 | break; 126 | case 'metafield': 127 | case 'metafields': 128 | $api = new Metafield($this); 129 | break; 130 | case 'order': 131 | case 'orders': 132 | $api = new Order($this); 133 | break; 134 | case 'product': 135 | case 'products': 136 | $api = new Product($this); 137 | break; 138 | case 'shop': 139 | $api = new Shop($this); 140 | break; 141 | case 'variant': 142 | case 'variants': 143 | $api = new Variants($this); 144 | break; 145 | case 'webhook': 146 | case 'webhooks': 147 | $api = new Webhook($this); 148 | break; 149 | default: 150 | throw new InvalidArgumentException(sprintf('Undefined api called: "%s"', $name)); 151 | } 152 | 153 | return $api; 154 | } 155 | 156 | /** 157 | * Authenticate a user for all next requests 158 | * 159 | * @see \ShopifyApi\Providers\ShopifyServiceProvider 160 | * 161 | * @param array $config 162 | * @return $this 163 | */ 164 | public function withShop(array $config = []) 165 | { 166 | // Guzzle does our REST client and is immutable 167 | $this->httpClient = new HttpClient($config); 168 | return $this; 169 | } 170 | 171 | /** 172 | * So we may access the Shopify Client with the Facade. 173 | * 174 | * @return $this 175 | */ 176 | public function getClient() 177 | { 178 | return $this; 179 | } 180 | 181 | /** 182 | * Get Http Client 183 | * 184 | * @return HttpClient 185 | */ 186 | public function getHttpClient() 187 | { 188 | return $this->httpClient; 189 | } 190 | 191 | /** 192 | * @return GuzzleClient 193 | */ 194 | public function getGuzzleClient() 195 | { 196 | return $this->getHttpClient()->getClient(); 197 | } 198 | 199 | /** 200 | * Set Http Client 201 | * 202 | * @param HttpClient $httpClient 203 | */ 204 | public function setHttpClient(HttpClient $httpClient) 205 | { 206 | $this->httpClient = $httpClient; 207 | } 208 | 209 | /** 210 | * @return array 211 | */ 212 | public function getOptions() 213 | { 214 | return $this->options; 215 | } 216 | 217 | /** 218 | * Get option by name 219 | * 220 | * @param string $name the option's name 221 | * 222 | * @return mixed 223 | * 224 | * @throws InvalidArgumentException 225 | */ 226 | public function getOption($name) 227 | { 228 | if (! array_key_exists($name, $this->options)) { 229 | throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); 230 | } 231 | 232 | return $this->options[$name]; 233 | } 234 | 235 | /** 236 | * Set option 237 | * 238 | * @param string $name 239 | * @param mixed $value 240 | * 241 | * @throws InvalidArgumentException if the option is not defined 242 | * @throws InvalidArgumentException if the api version is set to an unsupported one 243 | */ 244 | public function setOption($name, $value) 245 | { 246 | if (! array_key_exists($name, $this->options)) { 247 | throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); 248 | } 249 | 250 | $this->options[$name] = $value; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/Api/Webhook.php: -------------------------------------------------------------------------------- 1 | webhook_id = $webhook_id; 121 | } 122 | 123 | /** 124 | * Retrieve all Webhooks 125 | * 126 | * @link https://help.shopify.com/api/reference/webhook#index 127 | * 128 | * @param array $params 129 | * limit: default=15, max = 200, 130 | * page: default=1 131 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 132 | */ 133 | public function all(array $params = []) 134 | { 135 | return $this->get('/webhooks.json', $params); 136 | } 137 | 138 | /** 139 | * Get Count 140 | * 141 | * @link https://help.shopify.com/api/reference/webhook#count 142 | * 143 | * @param array $params 144 | * address: filter by URI of the webhooks post request, 145 | * topic: default=all 146 | * @return \Guzzle\Http\EntityBodyInterface|mixed|integer 147 | */ 148 | public function count(array $params = []) 149 | { 150 | $count = $this->get('/webhooks/count.json', $params); 151 | return isset($count['count']) ? $count['count'] : 0; 152 | } 153 | 154 | /** 155 | * Create a Webhook 156 | * 157 | * @link https://help.shopify.com/api/reference/webhook#create 158 | * 159 | * @param array $webhook optional attributes 160 | * @return array webhook info 161 | */ 162 | public function create(array $webhook = array()) 163 | { 164 | return $this->post("/webhooks.json", compact('webhook')); 165 | } 166 | 167 | /** 168 | * Find a webhook 169 | * 170 | * @link https://help.shopify.com/api/reference/webhook#show 171 | * 172 | * @param string $id the webhook's id 173 | * @param array $params optional attributes 174 | * @return array webhook 175 | */ 176 | public function show($id, array $params = []) 177 | { 178 | return $this->get("/webhooks/{$id}.json", $params); 179 | } 180 | 181 | 182 | /** 183 | * Update a Webhook 184 | * 185 | * @link https://help.shopify.com/api/reference/webhook#update 186 | * 187 | * @param string $id the webhook's id 188 | * @param array $params optional attributes 189 | * @return array webhook 190 | */ 191 | public function update($id, array $params = []) 192 | { 193 | return $this->put("/webhooks/{$id}.json", $params); 194 | } 195 | 196 | /** 197 | * Delete a webhook (delete or remove do the same) 198 | * 199 | * @link https://help.shopify.com/api/reference/webhook#delete 200 | * @param string $id the webhook's id 201 | * 202 | * @return array empty 203 | */ 204 | public function remove($id) 205 | { 206 | return parent::delete("/webhooks/{$id}.json"); 207 | } 208 | 209 | /** 210 | * Delete a webhook (delete or remove do the same) 211 | * 212 | * @link https://help.shopify.com/api/reference/webhook#delete 213 | * @param string $id the webhook's id 214 | * 215 | * @param array $parameters 216 | * @param array $request_headers 217 | * @return array empty 218 | */ 219 | public function delete($id, array $parameters = [], $request_headers = []) 220 | { 221 | return parent::delete("/webhooks/{$id}.json"); 222 | } 223 | 224 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Shopify API (Deprecated, uses Guzzle 3.9) 2 | 3 | > Please note: the **new version** using Guzzle 6.2 is [actively maintained here](https://github.com/danrichards/shopify) 4 | 5 | An object-oriented approach towards using the Shopify API. 6 | 7 | ## Supported Objects / Endpoints: 8 | 9 | * [CustomCollection](https://help.shopify.com/api/reference/customcollection) 10 | * [Discount](https://help.shopify.com/api/reference/discount) - Shopify Plus 11 | * [Fulfillment](https://help.shopify.com/api/reference/fulfillment) - via Order 12 | * [FulfillmentEvent](https://help.shopify.com/api/reference/fulfillmentevent) - via Order 13 | * [Metafield](https://help.shopify.com/api/reference/metafield) 14 | * [Order](https://help.shopify.com/api/reference/order) 15 | * [OrderRisk](https://help.shopify.com/api/reference/order_risks) - via Order 16 | * [Product](https://help.shopify.com/api/reference/product) 17 | * [ProductImage](https://help.shopify.com/api/reference/product_image) 18 | * [ProductVariant](https://help.shopify.com/api/reference/product_variant) 19 | * [Webhook](https://help.shopify.com/api/reference/webhook) 20 | 21 | ## Composer 22 | 23 | $ composer require dan/shopify-api v0.9.9.* 24 | 25 | ## Usage without Laravel 26 | 27 | ``` 28 | // Assumes setup of client with access token. 29 | $mgr = ShopifyApi\Manager::init($shop, $token); 30 | $mgr->getProduct($product_id = 123); // returns ShopifyApi/Models/Product 31 | 32 | // Alternatively, we may call methods on the API object. 33 | $mgr->api('products')->show($product_id = 123); // returns array 34 | 35 | See Facade usages for other methods available. 36 | ``` 37 | 38 | ## Usage with Laravel 39 | 40 | In your `config/app.php` 41 | 42 | ### Add the following to your `providers` array: 43 | 44 | ShopifyApi\Providers\ShopifyServiceProvider::class, 45 | 46 | ### Add the following to your `aliases` array: 47 | 48 | 'Shopify' => ShopifyApi\Support\ShopifyFacade::class, 49 | 50 | ### Replace following variables in your `.env` 51 | 52 | ``` 53 | SHOPIFY_DOMAIN=your-shop-name.myshopify.com 54 | SHOPIFY_TOKEN=your-token-here 55 | ``` 56 | 57 | ### Using the Facade gives you `Manager` 58 | 59 | Methods called on `Manager` will cascade down onto `Client` via the `__call` method. 60 | 61 | > If you're using Laravel, `Models` will return `\Illuminate\Support\Collection` instead of `array`. 62 | 63 | ``` 64 | Shopify::getProduct($product_id = 123); // returns ShopifyApi/Models/Product 65 | Shopify::getAllProducts(); // returns Collection|array of ShopifyApi/Models/Product 66 | 67 | Shopify::getVariant($variant_id = 456); // returns ShopifyApi/Models/Variant 68 | Shopify::getAllVariants($product_id = 123); // returns Collection|array of ShopifyApi/Models/Variant 69 | 70 | Shopify::getOrder($order_id = 789); // returns ShopifyApi/Models/Order 71 | Shopify::getAllOrders(); // returns a Collection|array of ShopifyApi/Models/Order 72 | 73 | Shopify::getMetafield($metafield_id = 123); // returns ShopifyApi/Models/Metafield 74 | 75 | Shopify::getDiscount($dicount_id = 123); // returns ShopifyApi/Models/Discount 76 | Shopify::getAllDiscounts(); // returns Collection|array of ShopifyApi/Models/Discount 77 | 78 | Shopify::getAllWebhooks(); // returns Collection|array of ShopifyApi/Models/Webhook 79 | 80 | // Alternatively, we may call methods on the API object. 81 | 82 | Shopify::api('products')->show($product_id = 123); // returns array 83 | Shopify::api('products')->all(); // returns array 84 | Shopify::api('products')->count(); // returns int 85 | 86 | Shopify::api('products')->metafields($product_id = '1234') // returns array 87 | 88 | Shopify::api('variants')->show($variant_id = 456); // returns array 89 | Shopify::api('variants')->product($product_id = 123)->all(); // returns array 90 | 91 | Shopify::api('orders')->show($order_id = 123); // returns array 92 | Shopify::api('orders')->all(); // returns array 93 | Shopify::api('orders')->count(); // returns int 94 | 95 | Shopify::api('custom_collections')->show($cc_id = 123); // returns array 96 | Shopify::api('custom_collections')->all(); // returns array 97 | Shopify::api('custom_collections')->count(); // returns int 98 | 99 | Shopify::api('discounts')->show($discount_id = 123); // returns array 100 | Shopify::api('discounts')->all(); // returns array 101 | Shopify::api('discounts')->count(); // returns int 102 | 103 | Shopify::api('webhooks')->show($webhook_id = 123); // returns array 104 | Shopify::api('webhooks')->all(); // returns array 105 | Shopify::api('webhooks')->count(); // returns int 106 | ``` 107 | 108 | #### Examples of saving data. 109 | 110 | ##### Creating a product using a model 111 | 112 | ``` 113 | $product = Shopify::getProduct(); 114 | $product->setTitle('Burton Custom Freestyle 151'); 115 | $product->setBodyHtml('Good snowboard!<\/strong>'); 116 | $product->setVendor('Burton'); 117 | $product->setProductType('Snowboard'); 118 | $product->setTags(['Barnes & Noble', 'John\'s Fav', '"Big Air"']); 119 | $product->save(); 120 | ``` 121 | 122 | ##### Updating a product using a model 123 | 124 | ``` 125 | $product = Shopify::getProduct(123); 126 | $product->setTitle('Burton Freestyle 152'); 127 | $product->save(); 128 | ``` 129 | 130 | ##### Add a product to a collection 131 | 132 | ``` 133 | $collection = Shopify::getCustomCollection(123); 134 | $collection->add(456); 135 | ``` 136 | 137 | or 138 | 139 | ``` 140 | $collection = Shopify::getCustomCollection(123); 141 | $collection->add([456,789]); 142 | ``` 143 | 144 | ##### Creating and updating metafields for resources is more intuitive using key / namespace. 145 | 146 | ``` 147 | // The 'value_type' property will be determined automatically if omitted 148 | $product->createMetafield('in_stock', 'inventory', ['value' => 123]); 149 | 150 | $product->updateMetafield('in_stock', 'inventory', ['value' => 122]); 151 | 152 | $product->updateOrCreateMetafield('in_stock', 'inventory', ['value' => 200]); 153 | 154 | // Support is included for arrays and objects (json encodable) and null 155 | $product->createMetafield('board_specs', 'criteria', ['value' => new MyJsonSerializableObject()]); 156 | ``` 157 | 158 | ## Embedded Apps 159 | 160 | #### Get a token for a redirect response. 161 | 162 | ``` 163 | Shopify::getAppInstallResponse( 164 | 'your_app_client_id', 165 | 'your_app_client_secret', 166 | 'shop_from_request', 167 | 'code_from_request' 168 | ); 169 | 170 | // returns (object) ['access_token' => '...', 'scopes' => '...'] 171 | ``` 172 | 173 | #### Verify App Hmac (works for callback or redirect) 174 | 175 | ``` 176 | ShopifyApi\Util::validAppHmac( 177 | 'hmac_from_request', 178 | 'your_app_client_secret', 179 | ['shop' => '...', 'timestamp' => '...', ...] 180 | ); 181 | ``` 182 | 183 | #### Verify App Webhook Hmac 184 | 185 | ``` 186 | ShopifyApi\Util::validWebhookHmac( 187 | 'hmac_from_request', 188 | 'your_app_client_secret', 189 | file_get_contents('php://input') 190 | ); 191 | ``` 192 | 193 | ## Caching 194 | 195 | If you want to sprinkle a little caching in, setup a service provider and extend the `\ShopifyApi\Providers\ShopifyServiceProvider`. 196 | 197 | #### Here is an example Provider: 198 | 199 | ``` 200 | setApiCache(Product::class, function($client, $params = null) { 227 | 228 | // No caching for collections. 229 | if (is_array($params)) { 230 | // Returning falsy will result in the default api behavior. 231 | return null; 232 | } 233 | 234 | $key = "shopify_product_".((string) $params); 235 | 236 | // Assuming you Cache::put($key, $product->getData()); elsewhere 237 | // If the cache is empty, the resource will be fetched from the api 238 | // as normal. 239 | $data = Cache::get($key); 240 | 241 | return $data ? new Product($client, $data) : null; 242 | }); 243 | } 244 | } 245 | ``` 246 | 247 | ## Contributors 248 | 249 | - [Diogo Gomes](https://github.com/diogogomeswww) 250 | 251 | ## Special Thanks 252 | 253 | This repository's structure was modeled after the robust [`cdaguerre/php-trello-api`](https://github.com/cdaguerre/php-trello-api). 254 | 255 | ## Todo 256 | 257 | * Migrate from `guzzle/guzzle` to `guzzlehttp/guzzle`, bump version. 258 | * Publish files for Laravel setup 259 | * Artisan Command to create token 260 | 261 | ## License 262 | 263 | MIT. 264 | -------------------------------------------------------------------------------- /src/Api/AbstractApi.php: -------------------------------------------------------------------------------- 1 | client = $client; 40 | } 41 | 42 | /** 43 | * Catches any undefined "get{$field}" calls, and passes them 44 | * to the getField() if the $field is in the $this->fields property 45 | * 46 | * @param string $method called method 47 | * @param array $arguments array of arguments passed to called method 48 | * @return array 49 | * 50 | * @throws BadMethodCallException If the method does not start with "get" 51 | * or the field is not included in the $fields property 52 | */ 53 | public function __call($method, $arguments) 54 | { 55 | if (isset($this->fields) && substr($method, 0, 3) === 'get') { 56 | $property = lcfirst(substr($method, 3)); 57 | if (in_array($property, $this->fields) && count($arguments) === 2) { 58 | return $this->getField($arguments[0], $arguments[1]); 59 | } 60 | } 61 | 62 | throw new BadMethodCallException(sprintf( 63 | 'There is no method named "%s" in class "%s".', 64 | $method, 65 | get_called_class() 66 | )); 67 | } 68 | 69 | /** 70 | * @return Client 71 | */ 72 | public function getClient() 73 | { 74 | return $this->client; 75 | } 76 | 77 | /** 78 | * Get field names (properties) 79 | * 80 | * @return array array of fields 81 | */ 82 | public function getFields() 83 | { 84 | return static::$fields; 85 | } 86 | 87 | /** 88 | * Get a field value by field name 89 | * 90 | * @param string $id the board's id 91 | * @param array|string $field the field 92 | * @return mixed field value 93 | * 94 | * @throws InvalidArgumentException If the field does not exist 95 | */ 96 | public function getField($id, $field) 97 | { 98 | if (!in_array($field, static::$fields)) { 99 | throw new InvalidArgumentException(sprintf('There is no field named %s.', $field)); 100 | } 101 | 102 | $response = $this->get($this->getPath($id), ['fields' => implode(',', (array) $field)]); 103 | 104 | if (array_key_exists($sub_key = static::$parameters_wrap, $response)) { 105 | $response = $response[$sub_key]; 106 | } 107 | 108 | if (count($response) == 1 && array_key_exists($field, $response)) { 109 | $response = $response[$field]; 110 | } 111 | 112 | return $response; 113 | } 114 | 115 | /** 116 | * @return string 117 | */ 118 | public function getParametersWrap() 119 | { 120 | return static::$parameters_wrap; 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getParametersWrapMany() 127 | { 128 | return static::$parameters_wrap_many; 129 | } 130 | 131 | /** 132 | * Send a GET request with query parameters. 133 | * 134 | * @param string $path Request path. 135 | * @param array $parameters GET parameters. 136 | * @param array $request_headers Request Headers. 137 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 138 | */ 139 | protected function get($path, array $parameters = array(), $request_headers = array()) 140 | { 141 | $response = $this->client->getHttpClient() 142 | ->get($path, $parameters, $request_headers); 143 | 144 | return Util::getContent($response); 145 | } 146 | 147 | /** 148 | * Send a POST request with JSON-encoded parameters. 149 | * 150 | * @param string $path Request path. 151 | * @param array $parameters POST parameters to be JSON encoded. 152 | * @param array $request_headers Request headers. 153 | * @return mixed 154 | */ 155 | protected function post($path, array $parameters = array(), $request_headers = array()) 156 | { 157 | return $this->postRaw( 158 | $path, 159 | $this->createParametersBody($parameters), 160 | $request_headers 161 | ); 162 | } 163 | 164 | /** 165 | * Send a POST request with raw data. 166 | * 167 | * @param string $path Request path. 168 | * @param mixed $body Request body. 169 | * @param array $request_headers Request headers. 170 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string 171 | */ 172 | protected function postRaw($path, $body, $request_headers = array()) 173 | { 174 | $response = $this->client->getHttpClient()->post( 175 | $path, 176 | $body, 177 | $request_headers 178 | ); 179 | 180 | return Util::getContent($response); 181 | } 182 | 183 | /** 184 | * Send a PATCH request with JSON-encoded parameters. 185 | * 186 | * @param string $path Request path. 187 | * @param array $parameters POST parameters to be JSON encoded. 188 | * @param array $request_headers Request headers. 189 | * @param bool $normalize 190 | * @return mixed 191 | */ 192 | protected function patch($path, array $parameters = [], $request_headers = [], $normalize = true) 193 | { 194 | if ($normalize) { 195 | $parameters = isset($parameters[static::$parameters_wrap]) 196 | ? $parameters : [static::$parameters_wrap => $parameters]; 197 | 198 | $parameters[static::$parameters_wrap] = $this 199 | ->createParametersBody($parameters[static::$parameters_wrap]); 200 | } 201 | 202 | $parameters[static::$parameters_wrap] = $this 203 | ->removeExcessParameters($parameters[static::$parameters_wrap]); 204 | 205 | $body = json_encode($parameters); 206 | 207 | $response = $this->client 208 | ->getHttpClient() 209 | ->put($path, $body, $request_headers); 210 | 211 | return Util::getContent($response); 212 | } 213 | 214 | /** 215 | * Send a PUT request with JSON-encoded parameters. 216 | * 217 | * @param string $path Request path. 218 | * @param array $parameters POST parameters to be JSON encoded. 219 | * @param array $request_headers Request headers. 220 | * @param bool $normalize 221 | * @return mixed 222 | */ 223 | protected function put($path, array $parameters = [], $request_headers = [], $normalize = true) 224 | { 225 | return $this->patch($path, $parameters, $request_headers, $normalize); 226 | } 227 | 228 | /** 229 | * Send a DELETE request with JSON-encoded parameters. 230 | * 231 | * @param string $path Request path. 232 | * @param array $parameters POST parameters to be JSON encoded. 233 | * @param array $request_headers Request headers. 234 | * @return mixed 235 | */ 236 | protected function delete($path, array $parameters = [], $request_headers = []) 237 | { 238 | $response = $this->client->getHttpClient()->delete( 239 | $path, 240 | $this->createParametersBody($parameters), 241 | $request_headers 242 | ); 243 | 244 | return Util::getContent($response); 245 | } 246 | 247 | /** 248 | * Prepare request parameters. 249 | * 250 | * @param array $parameters Request parameters 251 | * @return null|string 252 | */ 253 | protected function createParametersBody(array $parameters) 254 | { 255 | // Nothing is necessitated here right now. 256 | return $parameters; 257 | } 258 | 259 | /** 260 | * @param null $id 261 | * @return mixed|string 262 | */ 263 | protected function getPath($id = null, $path = null) 264 | { 265 | $path = $path ?: static::$path; 266 | 267 | if ($id) { 268 | return preg_replace('/\#id\#/', rawurlencode($id), $path); 269 | } 270 | 271 | return static::$path; 272 | } 273 | 274 | /** 275 | * Remove our fields that don't need to be passed to Trello. 276 | * This prevents trello from throwing a "Error parsing body: too many parameters" error 277 | * on requests with a card that has a lot of content. 278 | * 279 | * @param array $parameters 280 | * @return array 281 | */ 282 | protected function removeExcessParameters($parameters) 283 | { 284 | // Remove our fields that don't need to be passed to Trello. 285 | foreach(static::$ignore_on_update_fields as $ignored_field) { 286 | unset($parameters[$ignored_field]); 287 | } 288 | 289 | return $parameters; 290 | } 291 | 292 | /** 293 | * Take a shot as what the api_name should be? 294 | */ 295 | protected function guessApiName() 296 | { 297 | $name = explode("\\", get_class($this)); 298 | $name = strtolower(array_pop($name)); 299 | return $name; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/Models/Shop.php: -------------------------------------------------------------------------------- 1 | client = $client; 229 | 230 | $this->api = $client->api(static::$api_name); 231 | $this->fields = $this->api->getFields(); 232 | 233 | if (empty($data)) { 234 | $this->refresh(); 235 | } else { 236 | $this->setData($data); 237 | } 238 | } 239 | 240 | /** 241 | * @return $this 242 | */ 243 | public function refresh() 244 | { 245 | $this->preRefresh(); 246 | $this->data = $this->api->show(); 247 | if (is_array($this->data) && array_key_exists(static::$api_name, $this->data)) { 248 | $this->setData($this->data[static::$api_name]); 249 | } 250 | $this->postRefresh(); 251 | 252 | return $this; 253 | } 254 | 255 | /** 256 | * Update the object through API 257 | * 258 | * @return $this 259 | * @throws \BadMethodCallException 260 | */ 261 | protected function update() 262 | { 263 | throw new BadMethodCallException("Method not allowed on Shop resource."); 264 | } 265 | 266 | /** 267 | * Create the object through API 268 | * 269 | * @return $this 270 | */ 271 | protected function create() 272 | { 273 | throw new BadMethodCallException("Method not allowed on Shop resource."); 274 | } 275 | 276 | } -------------------------------------------------------------------------------- /src/Models/AbstractModel.php: -------------------------------------------------------------------------------- 1 | client = $client; 49 | 50 | // Skip api call (refresh) if we already have the data 51 | if (is_array($id_or_data)) { 52 | $this->api = $client->api( 53 | static::$api_name, 54 | isset($id_or_data['id']) ? $id_or_data['id'] : null 55 | ); 56 | $this->fields = $this->api->getFields(); 57 | $this->setData($id_or_data); 58 | 59 | // Otherwise, pull data from api 60 | } else { 61 | $this->api = $client->api(static::$api_name, $id_or_data); 62 | $this->fields = $this->api->getFields(); 63 | if ($id_or_data) { 64 | $this->id = $id_or_data; 65 | $this->refresh(); 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * @param $method 72 | * @param $arguments 73 | * @return mixed 74 | */ 75 | public function __call($method, $arguments) 76 | { 77 | $chunks = explode('_', Util::snake($method)); 78 | 79 | if (count($chunks) > 1) { 80 | $action = array_shift($chunks); 81 | $key = implode('_', $chunks); 82 | 83 | $strict = false; 84 | 85 | if (defined('SHOPIFY_API_MODE')) { 86 | if (SHOPIFY_API_MODE == 'strict') { 87 | $strict = true; 88 | } 89 | } 90 | 91 | if ($strict) { 92 | switch ($action) { 93 | case 'get': 94 | return $this->data[$key]; 95 | break; 96 | case 'set': 97 | $this->data[$key] = $arguments[0]; 98 | return $this; 99 | break; 100 | case 'has': 101 | return array_key_exists($key, $this->data); 102 | break; 103 | } 104 | } else { 105 | switch ($action) { 106 | case 'get': 107 | return array_key_exists($key, $this->data) 108 | ? $this->data[$key] : null; 109 | break; 110 | case 'set': 111 | $this->data[$key] = isset($arguments[0]) 112 | ? $arguments[0] : null; 113 | return $this; 114 | break; 115 | case 'has': 116 | return array_key_exists($key, $this->data); 117 | break; 118 | } 119 | } 120 | } 121 | 122 | throw new BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); 123 | } 124 | 125 | /** 126 | * @return Client 127 | */ 128 | public function getClient() 129 | { 130 | return $this->client; 131 | } 132 | 133 | /** 134 | * @return int 135 | */ 136 | public function getId() 137 | { 138 | return $this->id; 139 | } 140 | 141 | /** 142 | * @return array 143 | */ 144 | public function getData() 145 | { 146 | return $this->data; 147 | } 148 | 149 | /** 150 | * @param array $data 151 | * @return $this 152 | */ 153 | public function setData(array $data) 154 | { 155 | $this->id = isset($data['id']) ? $data['id'] : null; 156 | $this->data = $data; 157 | 158 | return $this; 159 | } 160 | 161 | /** 162 | * @param $name 163 | * @return mixed 164 | */ 165 | public function getOriginal($name) 166 | { 167 | return $this->data[$name]; 168 | } 169 | 170 | /** 171 | * @param $name 172 | * @param $value 173 | * @return mixed 174 | */ 175 | public function setOriginal($name, $value) 176 | { 177 | return $this->data[$name] = $value; 178 | } 179 | 180 | /** 181 | * @param DateTime|string 182 | * @return $this 183 | */ 184 | public function setCreatedAt($stringOrDateTime) 185 | { 186 | $this->data['created_at'] = $stringOrDateTime instanceof DateTime || $stringOrDateTime instanceof \Carbon\Carbon 187 | ? $stringOrDateTime->format('c') : $stringOrDateTime; 188 | 189 | return $this; 190 | } 191 | 192 | /** 193 | * @param DateTimeZone $time_zone 194 | * @return DateTime|null 195 | */ 196 | public function getCreatedAt(DateTimeZone $time_zone = null) 197 | { 198 | return is_null($date = $this->getOriginal('created_at')) 199 | ? $date : new DateTime($date, $time_zone); 200 | } 201 | 202 | /** 203 | * @param DateTimeZone $time_zone 204 | * @return DateTime|null 205 | */ 206 | public function getUpdatedAt(DateTimeZone $time_zone = null) 207 | { 208 | return is_null($date = $this->getOriginal('updated_at')) 209 | ? $date : new DateTime($date, $time_zone); 210 | } 211 | 212 | /** 213 | * @param DateTime|string 214 | * @return $this 215 | */ 216 | public function setUpdatedAt($stringOrDateTime) 217 | { 218 | $this->data['updated_at'] = $stringOrDateTime instanceof DateTime || $stringOrDateTime instanceof \Carbon\Carbon 219 | ? $stringOrDateTime->format('c') : $stringOrDateTime; 220 | 221 | return $this; 222 | } 223 | 224 | /** 225 | * @return $this 226 | */ 227 | public function refresh() 228 | { 229 | $this->preRefresh(); 230 | $this->data = $this->api->show($this->id, static::$load_params); 231 | if (is_array($this->data) && array_key_exists(static::$api_name, $this->data)) { 232 | $this->setData($this->data[static::$api_name]); 233 | } 234 | $this->postRefresh(); 235 | 236 | return $this; 237 | } 238 | 239 | /** 240 | * @return $this 241 | */ 242 | public function save() 243 | { 244 | try { 245 | $this->preSave(); 246 | $this->id ? $this->update() : $this->create(); 247 | $this->postSave(); 248 | } catch (BadMethodCallException $e) { 249 | throw new BadMethodCallException(sprintf( 250 | "You can't %s %s objects.", 251 | $this->id ? 'update' : 'create', 252 | get_called_class() 253 | )); 254 | } 255 | 256 | return $this->refresh(); 257 | } 258 | 259 | /** 260 | * @return $this 261 | */ 262 | public function remove() 263 | { 264 | try { 265 | $this->preRemove(); 266 | $this->api->remove($this->id); 267 | $this->postRemove(); 268 | } catch (BadMethodCallException $e) { 269 | throw new BadMethodCallException(sprintf( 270 | "You can't remove %s objects.", 271 | get_called_class() 272 | )); 273 | } 274 | 275 | return $this; 276 | } 277 | 278 | /** 279 | * Get multiple results from the Api and map them to Models 280 | * 281 | * @param array $params 282 | * @return array 283 | */ 284 | public function all(array $params = []) 285 | { 286 | $all = $this->api->all($params); 287 | 288 | if (! empty($all)) { 289 | $all = $all[$this->api->getParametersWrapMany()]; 290 | } 291 | 292 | $all = array_map(function($product) { 293 | return (new static($this->client))->setData($product); 294 | }, $all); 295 | 296 | return $all; 297 | } 298 | 299 | /** 300 | * Update the object through API 301 | * 302 | * @return $this 303 | */ 304 | protected function update() 305 | { 306 | $this->preUpdate(); 307 | $this->data = $this->api->update($this->id, $this->data); 308 | $this->postUpdate(); 309 | 310 | return $this; 311 | } 312 | 313 | /** 314 | * Create the object through API 315 | * 316 | * @return $this 317 | */ 318 | protected function create() 319 | { 320 | $this->preCreate(); 321 | $this->data = $this->api->create($this->data)[static::$api_name]; 322 | $this->id = $this->data['id']; 323 | $this->postCreate(); 324 | 325 | return $this; 326 | } 327 | 328 | /** 329 | * Called before saving (creating or updating) an entity 330 | */ 331 | protected function preSave() 332 | { 333 | $this->setUpdatedAt(new DateTime('now')); 334 | } 335 | 336 | /** 337 | * Called after saving (creating or updating) an entity 338 | */ 339 | protected function postSave() 340 | { 341 | } 342 | 343 | /** 344 | * Called before creating an entity 345 | */ 346 | protected function preCreate() 347 | { 348 | } 349 | 350 | /** 351 | * Called after creating an entity 352 | */ 353 | protected function postCreate() 354 | { 355 | } 356 | 357 | /** 358 | * Called before updating an entity 359 | */ 360 | protected function preUpdate() 361 | { 362 | } 363 | 364 | /** 365 | * Called after updating an entity 366 | */ 367 | protected function postUpdate() 368 | { 369 | } 370 | 371 | /** 372 | * Called before refreshing an entity 373 | */ 374 | protected function preRefresh() 375 | { 376 | } 377 | 378 | /** 379 | * Called after refreshing an entity 380 | */ 381 | protected function postRefresh() 382 | { 383 | } 384 | 385 | /** 386 | * Called before removing an entity 387 | */ 388 | protected function preRemove() 389 | { 390 | } 391 | 392 | /** 393 | * Called after removing an entity 394 | */ 395 | protected function postRemove() 396 | { 397 | } 398 | 399 | /** 400 | * @return string 401 | */ 402 | public function jsonSerialize () 403 | { 404 | return json_encode($this->getData()); 405 | } 406 | 407 | } 408 | -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | client = $client; 37 | } 38 | 39 | /** 40 | * @param string $method method name 41 | * @param array $args arguments 42 | * @return mixed 43 | * @throws BadMethodCallException 44 | */ 45 | public function __call($method, $args) 46 | { 47 | if (method_exists($this->client, $method)) { 48 | return call_user_func_array([$this->client, $method], $args); 49 | } else { 50 | throw new BadMethodCallException("The Manager __call method fires methods that exist on ".get_class($this->client)); 51 | } 52 | } 53 | 54 | /** 55 | * Get the instance with the Facade. Shopify::getInstance(); 56 | * 57 | * @return $this 58 | */ 59 | public function getInstance() 60 | { 61 | return $this; 62 | } 63 | 64 | /** 65 | * @param $shop 66 | * @param $token 67 | * @return Manager 68 | */ 69 | public function make($shop, $token) 70 | { 71 | return static::init($shop, $token); 72 | } 73 | 74 | /** 75 | * @param $shop 76 | * @param $token 77 | * @return Manager 78 | */ 79 | public static function init($shop, $token) 80 | { 81 | $base_url = preg_replace("/(https:\/\/|http:\/\/)/", "", $shop); 82 | $base_url = rtrim($base_url, "/"); 83 | $base_url = str_replace('.myshopify.com', '', $base_url); 84 | 85 | if (Util::isLaravel() && function_exists('config') && config('shopify_api.api_version')) { 86 | $api_version = config('shopify_api.api_version'); 87 | } else { 88 | $api_version = '2020-07'; 89 | } 90 | $base_url = "https://{$base_url}.myshopify.com/admin/api/{$api_version}"; 91 | 92 | // By default, let's setup our main shopify shop. 93 | $config = [ 94 | 'base_url' => $base_url, 95 | 'request.options' => [ 96 | 'headers' => [ 97 | 'X-Shopify-Access-Token' => $token, 98 | 'Accept' => 'application/json', 99 | 'Content-Type' => 'application/json; charset=utf-8;' 100 | ] 101 | ] 102 | ]; 103 | 104 | // Guzzle does our REST client and is immutable 105 | $guzzle_http_client = new GuzzleClient($config['base_url'], $config); 106 | 107 | // ShopifyHttpClient decorates GuzzleClient 108 | $shopify_http_client = new HttpClient($config, $guzzle_http_client); 109 | 110 | // ShopifyClient decorates our ShopifyHttpClient. We may swap in a 111 | // different client (ie. different shop) later, if need be. 112 | $shopify_client = new Client($shopify_http_client); 113 | 114 | // Manager is a singleton we may pull down out of the IoC container 115 | // with the active ShopifyClient we're working with. There is also 116 | // a facade `Shopify` which provides the Manager. 117 | return new static($shopify_client); 118 | } 119 | 120 | /** 121 | * Retrieve a token for someone installing your embedded Shopify App. 122 | * 123 | * @param string $client_id 124 | * @param string $client_secret 125 | * @param $shop 126 | * @param $code 127 | * @return \stdClass 128 | */ 129 | public function getAppInstallResponse($client_id, $client_secret, $shop, $code) 130 | { 131 | $base_url = "https://{$shop}/admin/oauth/access_token"; 132 | 133 | // By default, let's setup our main shopify shop. 134 | $config = [ 135 | 'base_url' => $base_url, 136 | 'request.options' => [ 137 | 'headers' => [ 138 | 'Accept' => 'application/json', 139 | 'Content-Type' => 'application/json; charset=utf-8;' 140 | ] 141 | ] 142 | ]; 143 | 144 | // Guzzle does our REST client and is immutable 145 | $guzzle_http_client = new GuzzleClient($config['base_url'], $config); 146 | 147 | // ShopifyHttpClient decorates GuzzleClient 148 | $shopify_http_client = new HttpClient($config, $guzzle_http_client); 149 | 150 | $params = compact('client_id', 'client_secret', 'code'); 151 | 152 | $response = $shopify_http_client->post($base_url, $params); 153 | 154 | return Util::getContent($response); 155 | } 156 | 157 | /** 158 | * Get a variant by id or create a new one 159 | * 160 | * @param int $id Collection id 161 | * @return Variant 162 | */ 163 | public function getCustomCollection($id = null) 164 | { 165 | return $this->fetchFromApiCache(Collection::class, $id) 166 | ?: new CustomCollection($this->client, $id); 167 | } 168 | 169 | /** 170 | * Get all the variants from a response as an array of Models or a 171 | * Collection of Models for Laravel. 172 | * 173 | * @param array $params 174 | * @return array|\Illuminate\Support\Collection 175 | */ 176 | public function getAllCustomCollections(array $params = []) 177 | { 178 | $collections = $this->fetchFromApiCache(Collection::class, $params) 179 | ?: (new CustomCollection($this->client))->all($params); 180 | 181 | return defined('LARAVEL_START') ? collect($collections) : $collections; 182 | } 183 | 184 | /** 185 | * Get a discount by id or create a new one 186 | * 187 | * @param int $id the Discount id 188 | * 189 | * @return Discount 190 | */ 191 | public function getDiscount($id = null) 192 | { 193 | return $this->fetchFromApiCache(Discount::class, $id) 194 | ?: new Discount($this->client, $id); 195 | } 196 | 197 | /** 198 | * Get all the discounts as an array of Models or a 199 | * Collection of Models for Laravel. 200 | * 201 | * @param array $params 202 | * @return \Illuminate\Support\Collection|array 203 | */ 204 | public function getAllDiscounts(array $params = []) 205 | { 206 | $discounts = $this->fetchFromApiCache(Discount::class, $params) 207 | ?: (new Discount($this->client))->all($params); 208 | 209 | return defined('LARAVEL_START') ? collect($discounts) : $discounts; 210 | } 211 | 212 | /** 213 | * Get a variant by id or create a new one 214 | * 215 | * @param int $id Metafield id 216 | * @return Metafield 217 | */ 218 | public function getMetafield($id = null) 219 | { 220 | return $this->fetchFromApiCache(Metafield::class, $id) 221 | ?: new Metafield($this->client, $id); 222 | } 223 | 224 | /** 225 | * Get an order by id or create a new one 226 | * 227 | * @param int $id the Order id 228 | * 229 | * @return Order 230 | */ 231 | public function getOrder($id = null) 232 | { 233 | return $this->fetchFromApiCache(Order::class, $id) 234 | ?: new Order($this->client, $id); 235 | } 236 | 237 | /** 238 | * Get all the orders from a response as an array of Models or a 239 | * Collection of Models for Laravel. 240 | * 241 | * @param array $params 242 | * @return \Illuminate\Support\Collection|array 243 | */ 244 | public function getAllOrders(array $params = []) 245 | { 246 | $orders = $this->fetchFromApiCache(Order::class, $params) 247 | ?: (new Order($this->client))->all($params); 248 | 249 | return defined('LARAVEL_START') ? collect($orders) : $orders; 250 | } 251 | 252 | /** 253 | * Get a product by id or create a new one 254 | * 255 | * @param int $id the Product id 256 | * 257 | * @return Product 258 | */ 259 | public function getProduct($id = null) 260 | { 261 | return $this->fetchFromApiCache(Product::class, $id) 262 | ?: new Product($this->client, $id); 263 | } 264 | 265 | /** 266 | * Get all the products from a response as an array of Models or a 267 | * Collection of Models for Laravel. 268 | * 269 | * @param array $params 270 | * @return \Illuminate\Support\Collection|array 271 | */ 272 | public function getAllProducts(array $params = []) 273 | { 274 | $products = $this->fetchFromApiCache(Product::class, $params) 275 | ?: (new Product($this->client))->all($params); 276 | 277 | return defined('LARAVEL_START') ? collect($products) : $products; 278 | } 279 | 280 | /** 281 | * Get a product by id or create a new one 282 | * 283 | * @param int $id the Product id 284 | * 285 | * @return Shop 286 | */ 287 | public function getShop() 288 | { 289 | return $this->fetchFromApiCache(Shop::class) 290 | ?: new Shop($this->client); 291 | } 292 | 293 | /** 294 | * Get a variant by id or create a new one 295 | * 296 | * @param int $id Variant id 297 | * @return Variant 298 | */ 299 | public function getVariant($id = null) 300 | { 301 | return $this->fetchFromApiCache(Variant::class, $id) 302 | ?: new Variant($this->client, $id); 303 | } 304 | 305 | /** 306 | * Get all the variants from a response as an array of Models or a 307 | * Collection of Models for Laravel. 308 | * 309 | * @param $product_id 310 | * @return array|\Illuminate\Support\Collection 311 | */ 312 | public function getAllVariants($product_id) 313 | { 314 | $variants = $this->getProduct($product_id)->variants(); 315 | return defined('LARAVEL_START') ? collect($variants) : $variants; 316 | } 317 | 318 | /** 319 | * Get an order by id or create a new one 320 | * 321 | * @param int $id the Webhook id 322 | * @return Webhook 323 | */ 324 | public function getWebhook($id = null) 325 | { 326 | return $this->fetchFromApiCache(Webhook::class, $id) 327 | ?: new Webhook($this->client, $id); 328 | } 329 | 330 | /** 331 | * Get all the webhooks from a response as an array of Models or a 332 | * Collection of Models for Laravel. 333 | * 334 | * @param array $params 335 | * @return \Illuminate\Support\Collection|array 336 | */ 337 | public function getAllWebhooks(array $params = []) 338 | { 339 | $webhooks = $this->fetchFromApiCache(Webhook::class, $params) 340 | ?: (new Webhook($this->client))->all($params); 341 | 342 | return defined('LARAVEL_START') ? collect($webhooks) : $webhooks; 343 | } 344 | 345 | /** 346 | * @param string $model 347 | * @param callable $callback 348 | */ 349 | public function setApiCache($model, callable $callback) { 350 | $this->api_cache[$model] = $callback; 351 | } 352 | 353 | /** 354 | * @param $model 355 | * @param $params 356 | * @return mixed 357 | */ 358 | public function fetchFromApiCache($model, $params = null) 359 | { 360 | if (isset($this->api_cache[$model])) { 361 | return $this->api_cache[$model]($this->getClient(), $params); 362 | } 363 | 364 | return null; 365 | } 366 | 367 | } 368 | -------------------------------------------------------------------------------- /src/Api/Order.php: -------------------------------------------------------------------------------- 1 | get('/orders.json', $params); 107 | } 108 | 109 | /** 110 | * Retrieve the number of orders 111 | * 112 | * @link https://help.shopify.com/api/reference/order#count 113 | * 114 | * @param array $params 115 | * @return array 116 | */ 117 | public function count(array $params = []) 118 | { 119 | $count = $this->get('/orders/count.json', $params); 120 | return isset($count['count']) 121 | ? $count['count'] : 0; 122 | } 123 | 124 | /** 125 | * Find an Order 126 | * 127 | * @link https://help.shopify.com/api/reference/order#show 128 | * 129 | * @param string $id the board's id 130 | * @param array $params optional attributes 131 | * @return array 132 | */ 133 | public function show($id, array $params = []) 134 | { 135 | return $this->get($this->getPath($id), $params); 136 | } 137 | 138 | /** 139 | * Create an Order 140 | * 141 | * @link https://help.shopify.com/api/reference/order#create 142 | * 143 | * @param array $params Attributes 144 | * @return array 145 | */ 146 | public function create(array $params = array()) 147 | { 148 | $order = $params; 149 | 150 | return $this->post('/orders.json', compact('order')); 151 | } 152 | 153 | /** 154 | * Update an Order 155 | * 156 | * @link https://help.shopify.com/api/reference/order#update 157 | * 158 | * @param $id 159 | * @param array $params 160 | * @return array 161 | */ 162 | public function update($id, array $params = []) 163 | { 164 | return $this->put($this->getPath(rawurlencode($id)), $params); 165 | } 166 | 167 | /** 168 | * @param $id 169 | * @param array $params 170 | * @return array 171 | */ 172 | public function remove($id, array $params = []) 173 | { 174 | return $this->delete($this->getPath(rawurlencode($id)), $params); 175 | } 176 | 177 | // ------------------------------------------------------------------------ 178 | // SUPPORT FOR ORDER RISKS 179 | // ------------------------------------------------------------------------ 180 | 181 | /** 182 | * Retrieve all risks for an order 183 | * 184 | * @link https://help.shopify.com/api/reference/order_risks#index 185 | * 186 | * @param $id 187 | * @param array $params 188 | * @return array 189 | */ 190 | public function risks($id, array $params = []) 191 | { 192 | $alt_path = '/orders/#id#/risks.json'; 193 | return $this->get($this->getPath($id, $alt_path), $params); 194 | } 195 | 196 | /** 197 | * @link https://help.shopify.com/api/reference/order_risks#create 198 | * 199 | * @param $id 200 | * @param array $params 201 | * @return array 202 | */ 203 | public function createRisk($id, array $params = []) 204 | { 205 | $alt_path = '/orders/#id#/risks.json'; 206 | $risk = $params; 207 | return $this->post($this->getPath($id, $alt_path), compact('risk')); 208 | } 209 | 210 | /** 211 | * @link https://help.shopify.com/api/reference/order_risks#update 212 | * 213 | * @param $id 214 | * @param $risk_id 215 | * @param array $params 216 | * @return array 217 | */ 218 | public function updateRisk($id, $risk_id, array $params = []) 219 | { 220 | $alt_path = "/orders/#id#/risks/{$risk_id}.json"; 221 | $risk = $params; 222 | return $this->post($this->getPath($id, $alt_path), compact('risk')); 223 | } 224 | 225 | /** 226 | * @link https://help.shopify.com/api/reference/order_risks#show 227 | * 228 | * @param $id 229 | * @param $risk_id 230 | * @return array 231 | */ 232 | public function showRisk($id, $risk_id) 233 | { 234 | $alt_path = "/orders/#id#/risks/{$risk_id}.json"; 235 | return $this->get($this->getPath($id, $alt_path)); 236 | } 237 | 238 | /** 239 | * @link https://help.shopify.com/api/reference/order_risks#destroy 240 | * 241 | * @param $id 242 | * @param $risk_id 243 | * @return array 244 | */ 245 | public function deleteRisk($id, $risk_id) 246 | { 247 | $alt_path = "/orders/#id#/risks/{$risk_id}.json"; 248 | return $this->delete($this->getPath($id, $alt_path)); 249 | } 250 | 251 | // ------------------------------------------------------------------------ 252 | // SUPPORT FOR ORDER FULFILLMENTS 253 | // ------------------------------------------------------------------------ 254 | 255 | /** 256 | * Retrieve all fulfillments for an order 257 | * 258 | * @link https://help.shopify.com/api/reference/fulfillment#index 259 | * 260 | * @param $id 261 | * @param array $params 262 | * @return array 263 | */ 264 | public function fulfillments($id, array $params = []) 265 | { 266 | $alt_path = '/orders/#id#/fulfillments.json'; 267 | return $this->get($this->getPath($id, $alt_path), $params); 268 | } 269 | 270 | /** 271 | * @link https://help.shopify.com/api/reference/fulfillment#create 272 | * 273 | * @param $id 274 | * @param array $params 275 | * @return array 276 | */ 277 | public function createFulfillment($id, array $params = []) 278 | { 279 | $alt_path = '/orders/#id#/fulfillments.json'; 280 | $fulfillment = $params; 281 | return $this->post($this->getPath($id, $alt_path), compact('fulfillment')); 282 | } 283 | 284 | /** 285 | * @link https://help.shopify.com/api/reference/fulfillment#update 286 | * 287 | * @param $id 288 | * @param $fulfillment_id 289 | * @param array $params 290 | * @return array 291 | */ 292 | public function updateFulfillment($id, $fulfillment_id, array $params = []) 293 | { 294 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}.json"; 295 | $fulfillment = $params; 296 | return $this->post($this->getPath($id, $alt_path), compact('fulfillment')); 297 | } 298 | 299 | /** 300 | * @link https://help.shopify.com/api/reference/fulfillment#show 301 | * 302 | * @param $id 303 | * @param $fulfillment_id 304 | * @return array 305 | */ 306 | public function showFulfillment($id, $fulfillment_id) 307 | { 308 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}.json"; 309 | return $this->get($this->getPath($id, $alt_path)); 310 | } 311 | 312 | /** 313 | * @link https://help.shopify.com/api/reference/fulfillment#count 314 | * 315 | * @param $id 316 | * @param array $params 317 | * @return array 318 | */ 319 | public function countFulfillments($id, array $params = []) 320 | { 321 | $alt_path = "/orders/#id#/fulfillments/count.json"; 322 | return $this->get($this->getPath($id, $alt_path), $params); 323 | } 324 | 325 | /** 326 | * @link https://help.shopify.com/api/reference/fulfillment#open 327 | * 328 | * @param $id 329 | * @param $fulfillment_id 330 | * @return array 331 | */ 332 | public function openFulfillment($id, $fulfillment_id) 333 | { 334 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/open.json"; 335 | return $this->post($this->getPath($id, $alt_path)); 336 | } 337 | 338 | /** 339 | * @link https://help.shopify.com/api/reference/fulfillment#complete 340 | * 341 | * @param $id 342 | * @param $fulfillment_id 343 | * @return array 344 | */ 345 | public function completeFulfillment($id, $fulfillment_id) 346 | { 347 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/complete.json"; 348 | return $this->post($this->getPath($id, $alt_path)); 349 | } 350 | 351 | /** 352 | * @link https://help.shopify.com/api/reference/fulfillment#cancel 353 | * 354 | * @param $id 355 | * @param $fulfillment_id 356 | * @return array 357 | */ 358 | public function cancelFulfillment($id, $fulfillment_id) 359 | { 360 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/cancel.json"; 361 | return $this->post($this->getPath($id, $alt_path)); 362 | } 363 | 364 | // ------------------------------------------------------------------------ 365 | // SUPPORT FOR ORDER FULFILLMENT EVENTS 366 | // ------------------------------------------------------------------------ 367 | 368 | /** 369 | * Retrieve all fulfillment events for an order 370 | * 371 | * @link https://help.shopify.com/api/reference/fulfillmentevent#index 372 | * 373 | * @param $id 374 | * @param $fulfillment_id 375 | * @param array $params 376 | * @return array 377 | */ 378 | public function fulfillmentEvents($id, $fulfillment_id, array $params = []) 379 | { 380 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/events.json"; 381 | return $this->get($this->getPath($id, $alt_path), $params); 382 | } 383 | 384 | /** 385 | * @link https://help.shopify.com/api/reference/fulfillmentevent#create 386 | * 387 | * @param $id 388 | * @param $fulfillment_id 389 | * @param array $params 390 | * @return array 391 | */ 392 | public function createFulfillmentEvent($id, $fulfillment_id, array $params = []) 393 | { 394 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/events.json"; 395 | $event = $params; 396 | return $this->post($this->getPath($id, $alt_path), compact('event')); 397 | } 398 | 399 | /** 400 | * @link https://help.shopify.com/api/reference/fulfillmentevent#show 401 | * 402 | * @param $id 403 | * @param $fulfillment_id 404 | * @param $event_id 405 | * @return array 406 | */ 407 | public function showFulfillmentEvent($id, $fulfillment_id, $event_id) 408 | { 409 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/events/{$event_id}.json"; 410 | return $this->get($this->getPath($id, $alt_path)); 411 | } 412 | 413 | /** 414 | * @link https://help.shopify.com/api/reference/fulfillmentevent#destroy 415 | * 416 | * @param $id 417 | * @param $fulfillment_id 418 | * @param $event_id 419 | * @return array 420 | */ 421 | public function deleteFulfillmentEvent($id, $fulfillment_id, $event_id) 422 | { 423 | $alt_path = "/orders/#id#/fulfillments/{$fulfillment_id}/events/{$event_id}.json"; 424 | return $this->delete($this->getPath($id, $alt_path)); 425 | } 426 | 427 | } 428 | -------------------------------------------------------------------------------- /src/Models/Order.php: -------------------------------------------------------------------------------- 1 | getShippingLines(); 233 | 234 | return floatval(array_reduce( 235 | $shipping_lines, 236 | function($total, $line) { 237 | return $total 238 | + (isset($line['price']) ? $line['price'] : 0) 239 | + array_reduce($line['tax_lines'], 240 | function($tax_total, $tax_line) { 241 | return $tax_total 242 | + (isset($tax_line['price']) ? $tax_line['price'] : 0); 243 | }, 244 | 0); 245 | }, 246 | 0)); 247 | } 248 | 249 | /** 250 | * @return int 251 | */ 252 | public function getTotalLineItems() 253 | { 254 | return count($this->getLineItems()); 255 | } 256 | 257 | /** 258 | * @return DateTime|null 259 | */ 260 | public function getClosedAt() 261 | { 262 | return is_null($date = $this->getOriginal('closed_at')) 263 | ? $date : new DateTime($date); 264 | } 265 | 266 | /** 267 | * @param DateTime|string 268 | * @return $this 269 | */ 270 | public function setClosedAt($stringOrDateTime) 271 | { 272 | $this->data['closed_at'] = $stringOrDateTime instanceof DateTime || $stringOrDateTime instanceof \Carbon\Carbon 273 | ? $stringOrDateTime->format('c') : $stringOrDateTime; 274 | 275 | return $this; 276 | } 277 | 278 | /** 279 | * @return DateTime|null 280 | */ 281 | public function getCancelledAt() 282 | { 283 | return is_null($date = $this->getOriginal('cancelled_at')) 284 | ? $date : new DateTime($date); 285 | } 286 | 287 | /** 288 | * @param DateTime|string 289 | * @return $this 290 | */ 291 | public function setCancelledAt($stringOrDateTime) 292 | { 293 | $this->data['cancelled_at'] = $stringOrDateTime instanceof DateTime || $stringOrDateTime instanceof \Carbon\Carbon 294 | ? $stringOrDateTime->format('c') : $stringOrDateTime; 295 | 296 | return $this; 297 | } 298 | 299 | /** 300 | * @return DateTime|null 301 | */ 302 | public function getProcessedAt() 303 | { 304 | return is_null($date = $this->getOriginal('processed_at')) 305 | ? $date : new DateTime($date); 306 | } 307 | 308 | /** 309 | * @param DateTime|string 310 | * @return $this 311 | */ 312 | public function setProcessedAt($stringOrDateTime) 313 | { 314 | $this->data['processed_at'] = $stringOrDateTime instanceof DateTime || $stringOrDateTime instanceof \Carbon\Carbon 315 | ? $stringOrDateTime->format('c') : $stringOrDateTime; 316 | 317 | return $this; 318 | } 319 | 320 | // ------------------------------------------------------------------------ 321 | // SUPPORT FOR ORDER RISKS 322 | // ------------------------------------------------------------------------ 323 | 324 | /** 325 | * Retrieve all risks for an order 326 | * 327 | * @link https://help.shopify.com/api/reference/order_risks#index 328 | * 329 | * @param array $params 330 | * @return array 331 | */ 332 | public function risks(array $params = []) 333 | { 334 | $response = $this->api->risks($this->getId(), $params); 335 | return isset($response['risks']) ? $response['risks'] : $response; 336 | } 337 | 338 | /** 339 | * @param array $params 340 | * @return array 341 | */ 342 | public function createRisk(array $params = []) 343 | { 344 | $response = $this->api->createRisk($this->getId(), $params); 345 | return isset($response['risk']) ? $response['risk'] : $response; 346 | } 347 | 348 | /** 349 | * @param $risk_id 350 | * @param array $params 351 | * @return array 352 | */ 353 | public function updateRisk($risk_id, array $params = []) 354 | { 355 | $response = $this->api->updateRisk($this->getId(), $risk_id, $params); 356 | return isset($response['risk']) ? $response['risk'] : $response; 357 | } 358 | 359 | /** 360 | * @param $risk_id 361 | * @return array 362 | */ 363 | public function showRisk($risk_id) 364 | { 365 | $response = $this->api->showRisk($this->getId(), $risk_id); 366 | return isset($response['risk']) ? $response['risk'] : $response; 367 | } 368 | 369 | /** 370 | * @param $risk_id 371 | * @return array 372 | */ 373 | public function deleteRisk($risk_id) 374 | { 375 | return $this->api->deleteRisk($this->getId(), $risk_id); 376 | } 377 | 378 | // ------------------------------------------------------------------------ 379 | // SUPPORT FOR ORDER FULFILLMENTS 380 | // ------------------------------------------------------------------------ 381 | 382 | /** 383 | * Retrieve all risks for an order 384 | * 385 | * @link https://help.shopify.com/api/reference/order_risks#index 386 | * 387 | * @param array $params 388 | * @return array 389 | */ 390 | public function fulfillments(array $params = []) 391 | { 392 | $response = $this->api->fulfillments($this->getId(), $params); 393 | return isset($response['fulfillments']) 394 | ? $response['fulfillments'] 395 | : $response; 396 | } 397 | 398 | /** 399 | * @param array $params 400 | * @return array 401 | */ 402 | public function createFulfillment(array $params = []) 403 | { 404 | $response = $this->api->createFulfillment($this->getId(), $params); 405 | return isset($response['fulfillment']) 406 | ? $response['fulfillment'] 407 | : $response; 408 | } 409 | 410 | /** 411 | * @param $fulfillment_id 412 | * @param array $params 413 | * @return array 414 | */ 415 | public function updateFulfillment($fulfillment_id, array $params = []) 416 | { 417 | $response = $this->api->updateFulfillment($this->getId(), $fulfillment_id, $params); 418 | return isset($response['fulfillment']) 419 | ? $response['fulfillment'] 420 | : $response; 421 | } 422 | 423 | /** 424 | * @param $fulfillment_id 425 | * @param array $params 426 | * @return array 427 | */ 428 | public function showFulfillment($fulfillment_id, array $params = []) 429 | { 430 | $response = $this->api->showFulfillment($this->getId(), $fulfillment_id, $params); 431 | return isset($response['fulfillment']) 432 | ? $response['fulfillment'] 433 | : $response; 434 | } 435 | 436 | /** 437 | * @param array $params 438 | * @return integer 439 | */ 440 | public function countFulfillments(array $params = []) 441 | { 442 | $response = $this->api->countFulfillments($this->getId(), $params); 443 | return isset($response['count']) 444 | ? $response['count'] 445 | : $response; 446 | } 447 | 448 | /** 449 | * @param $fulfillment_id 450 | * @return array 451 | */ 452 | public function openFulfillment($fulfillment_id) 453 | { 454 | $response = $this->api->openFulfillment($this->getId(), $fulfillment_id); 455 | return isset($response['fulfillment']) 456 | ? $response['fulfillment'] 457 | : $response; 458 | } 459 | 460 | /** 461 | * @param $fulfillment_id 462 | * @return array 463 | */ 464 | public function completeFulfillment($fulfillment_id) 465 | { 466 | $response = $this->api->completeFulfillment($this->getId(), $fulfillment_id); 467 | return isset($response['fulfillment']) 468 | ? $response['fulfillment'] 469 | : $response; 470 | } 471 | 472 | /** 473 | * @param $fulfillment_id 474 | * @return array 475 | */ 476 | public function cancelFulfillment($fulfillment_id) 477 | { 478 | $response = $this->api->cancelFulfillment($this->getId(), $fulfillment_id); 479 | return isset($response['fulfillment']) 480 | ? $response['fulfillment'] 481 | : $response; 482 | } 483 | 484 | // ------------------------------------------------------------------------ 485 | // SUPPORT FOR ORDER FULFILLMENT EVENTS 486 | // ------------------------------------------------------------------------ 487 | 488 | /** 489 | * Retrieve all risks for an order 490 | * 491 | * @link https://help.shopify.com/api/reference/order_risks#index 492 | * 493 | * @param $fulfillment_id 494 | * @param array $params 495 | * @return array 496 | */ 497 | public function fulfillmentEvents($fulfillment_id, array $params = []) 498 | { 499 | $response = $this->api->fulfillmentEvents($this->getId(), $fulfillment_id, $params); 500 | return isset($response['fulfillment_events']) ? $response['fulfillment_events'] : $response; 501 | } 502 | 503 | /** 504 | * @param $fulfillment_id 505 | * @param array $params 506 | * @return array 507 | */ 508 | public function createFulfillmentEvent($fulfillment_id, array $params = []) 509 | { 510 | $response = $this->api->createFulfillment($this->getId(), $fulfillment_id, $params); 511 | return isset($response['fulfillment_event']) 512 | ? $response['fulfillment_event'] 513 | : $response; 514 | } 515 | 516 | /** 517 | * @param $fulfillment_id 518 | * @param $event_id 519 | * @return array 520 | */ 521 | public function showFulfillmentEvent($fulfillment_id, $event_id) 522 | { 523 | $response = $this->api->showFulfillmentEvent($this->getId(), $fulfillment_id, $event_id); 524 | return isset($response['fulfillment_event']) 525 | ? $response['fulfillment_event'] 526 | : $response; 527 | } 528 | 529 | /** 530 | * @param $fulfillment_id 531 | * @param $event_id 532 | * @return array 533 | */ 534 | public function deleteFulfillmentEvent($fulfillment_id, $event_id) 535 | { 536 | return $this->api->deleteFulfillmentEvent($this->getId(), $fulfillment_id, $event_id); 537 | } 538 | 539 | } 540 | -------------------------------------------------------------------------------- /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 | "hash": "85852c8f6dd0d48fb2a7f2ae5668178e", 8 | "content-hash": "c2035eca52853b24fe8231e7389fc0cc", 9 | "packages": [ 10 | { 11 | "name": "guzzle/guzzle", 12 | "version": "v3.9.3", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/guzzle/guzzle3.git", 16 | "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", 21 | "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "ext-curl": "*", 26 | "php": ">=5.3.3", 27 | "symfony/event-dispatcher": "~2.1" 28 | }, 29 | "replace": { 30 | "guzzle/batch": "self.version", 31 | "guzzle/cache": "self.version", 32 | "guzzle/common": "self.version", 33 | "guzzle/http": "self.version", 34 | "guzzle/inflection": "self.version", 35 | "guzzle/iterator": "self.version", 36 | "guzzle/log": "self.version", 37 | "guzzle/parser": "self.version", 38 | "guzzle/plugin": "self.version", 39 | "guzzle/plugin-async": "self.version", 40 | "guzzle/plugin-backoff": "self.version", 41 | "guzzle/plugin-cache": "self.version", 42 | "guzzle/plugin-cookie": "self.version", 43 | "guzzle/plugin-curlauth": "self.version", 44 | "guzzle/plugin-error-response": "self.version", 45 | "guzzle/plugin-history": "self.version", 46 | "guzzle/plugin-log": "self.version", 47 | "guzzle/plugin-md5": "self.version", 48 | "guzzle/plugin-mock": "self.version", 49 | "guzzle/plugin-oauth": "self.version", 50 | "guzzle/service": "self.version", 51 | "guzzle/stream": "self.version" 52 | }, 53 | "require-dev": { 54 | "doctrine/cache": "~1.3", 55 | "monolog/monolog": "~1.0", 56 | "phpunit/phpunit": "3.7.*", 57 | "psr/log": "~1.0", 58 | "symfony/class-loader": "~2.1", 59 | "zendframework/zend-cache": "2.*,<2.3", 60 | "zendframework/zend-log": "2.*,<2.3" 61 | }, 62 | "suggest": { 63 | "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." 64 | }, 65 | "type": "library", 66 | "extra": { 67 | "branch-alias": { 68 | "dev-master": "3.9-dev" 69 | } 70 | }, 71 | "autoload": { 72 | "psr-0": { 73 | "Guzzle": "src/", 74 | "Guzzle\\Tests": "tests/" 75 | } 76 | }, 77 | "notification-url": "https://packagist.org/downloads/", 78 | "license": [ 79 | "MIT" 80 | ], 81 | "authors": [ 82 | { 83 | "name": "Michael Dowling", 84 | "email": "mtdowling@gmail.com", 85 | "homepage": "https://github.com/mtdowling" 86 | }, 87 | { 88 | "name": "Guzzle Community", 89 | "homepage": "https://github.com/guzzle/guzzle/contributors" 90 | } 91 | ], 92 | "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", 93 | "homepage": "http://guzzlephp.org/", 94 | "keywords": [ 95 | "client", 96 | "curl", 97 | "framework", 98 | "http", 99 | "http client", 100 | "rest", 101 | "web service" 102 | ], 103 | "abandoned": "guzzlehttp/guzzle", 104 | "time": "2015-03-18 18:23:50" 105 | }, 106 | { 107 | "name": "symfony/event-dispatcher", 108 | "version": "v2.8.12", 109 | "source": { 110 | "type": "git", 111 | "url": "https://github.com/symfony/event-dispatcher.git", 112 | "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8" 113 | }, 114 | "dist": { 115 | "type": "zip", 116 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/889983a79a043dfda68f38c38b6dba092dd49cd8", 117 | "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8", 118 | "shasum": "" 119 | }, 120 | "require": { 121 | "php": ">=5.3.9" 122 | }, 123 | "require-dev": { 124 | "psr/log": "~1.0", 125 | "symfony/config": "~2.0,>=2.0.5|~3.0.0", 126 | "symfony/dependency-injection": "~2.6|~3.0.0", 127 | "symfony/expression-language": "~2.6|~3.0.0", 128 | "symfony/stopwatch": "~2.3|~3.0.0" 129 | }, 130 | "suggest": { 131 | "symfony/dependency-injection": "", 132 | "symfony/http-kernel": "" 133 | }, 134 | "type": "library", 135 | "extra": { 136 | "branch-alias": { 137 | "dev-master": "2.8-dev" 138 | } 139 | }, 140 | "autoload": { 141 | "psr-4": { 142 | "Symfony\\Component\\EventDispatcher\\": "" 143 | }, 144 | "exclude-from-classmap": [ 145 | "/Tests/" 146 | ] 147 | }, 148 | "notification-url": "https://packagist.org/downloads/", 149 | "license": [ 150 | "MIT" 151 | ], 152 | "authors": [ 153 | { 154 | "name": "Fabien Potencier", 155 | "email": "fabien@symfony.com" 156 | }, 157 | { 158 | "name": "Symfony Community", 159 | "homepage": "https://symfony.com/contributors" 160 | } 161 | ], 162 | "description": "Symfony EventDispatcher Component", 163 | "homepage": "https://symfony.com", 164 | "time": "2016-07-28 16:56:28" 165 | } 166 | ], 167 | "packages-dev": [ 168 | { 169 | "name": "doctrine/instantiator", 170 | "version": "1.0.5", 171 | "source": { 172 | "type": "git", 173 | "url": "https://github.com/doctrine/instantiator.git", 174 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 175 | }, 176 | "dist": { 177 | "type": "zip", 178 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 179 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 180 | "shasum": "" 181 | }, 182 | "require": { 183 | "php": ">=5.3,<8.0-DEV" 184 | }, 185 | "require-dev": { 186 | "athletic/athletic": "~0.1.8", 187 | "ext-pdo": "*", 188 | "ext-phar": "*", 189 | "phpunit/phpunit": "~4.0", 190 | "squizlabs/php_codesniffer": "~2.0" 191 | }, 192 | "type": "library", 193 | "extra": { 194 | "branch-alias": { 195 | "dev-master": "1.0.x-dev" 196 | } 197 | }, 198 | "autoload": { 199 | "psr-4": { 200 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 201 | } 202 | }, 203 | "notification-url": "https://packagist.org/downloads/", 204 | "license": [ 205 | "MIT" 206 | ], 207 | "authors": [ 208 | { 209 | "name": "Marco Pivetta", 210 | "email": "ocramius@gmail.com", 211 | "homepage": "http://ocramius.github.com/" 212 | } 213 | ], 214 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 215 | "homepage": "https://github.com/doctrine/instantiator", 216 | "keywords": [ 217 | "constructor", 218 | "instantiate" 219 | ], 220 | "time": "2015-06-14 21:17:01" 221 | }, 222 | { 223 | "name": "phpdocumentor/reflection-common", 224 | "version": "1.0", 225 | "source": { 226 | "type": "git", 227 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 228 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" 229 | }, 230 | "dist": { 231 | "type": "zip", 232 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 233 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 234 | "shasum": "" 235 | }, 236 | "require": { 237 | "php": ">=5.5" 238 | }, 239 | "require-dev": { 240 | "phpunit/phpunit": "^4.6" 241 | }, 242 | "type": "library", 243 | "extra": { 244 | "branch-alias": { 245 | "dev-master": "1.0.x-dev" 246 | } 247 | }, 248 | "autoload": { 249 | "psr-4": { 250 | "phpDocumentor\\Reflection\\": [ 251 | "src" 252 | ] 253 | } 254 | }, 255 | "notification-url": "https://packagist.org/downloads/", 256 | "license": [ 257 | "MIT" 258 | ], 259 | "authors": [ 260 | { 261 | "name": "Jaap van Otterdijk", 262 | "email": "opensource@ijaap.nl" 263 | } 264 | ], 265 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 266 | "homepage": "http://www.phpdoc.org", 267 | "keywords": [ 268 | "FQSEN", 269 | "phpDocumentor", 270 | "phpdoc", 271 | "reflection", 272 | "static analysis" 273 | ], 274 | "time": "2015-12-27 11:43:31" 275 | }, 276 | { 277 | "name": "phpdocumentor/reflection-docblock", 278 | "version": "3.1.1", 279 | "source": { 280 | "type": "git", 281 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 282 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" 283 | }, 284 | "dist": { 285 | "type": "zip", 286 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", 287 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", 288 | "shasum": "" 289 | }, 290 | "require": { 291 | "php": ">=5.5", 292 | "phpdocumentor/reflection-common": "^1.0@dev", 293 | "phpdocumentor/type-resolver": "^0.2.0", 294 | "webmozart/assert": "^1.0" 295 | }, 296 | "require-dev": { 297 | "mockery/mockery": "^0.9.4", 298 | "phpunit/phpunit": "^4.4" 299 | }, 300 | "type": "library", 301 | "autoload": { 302 | "psr-4": { 303 | "phpDocumentor\\Reflection\\": [ 304 | "src/" 305 | ] 306 | } 307 | }, 308 | "notification-url": "https://packagist.org/downloads/", 309 | "license": [ 310 | "MIT" 311 | ], 312 | "authors": [ 313 | { 314 | "name": "Mike van Riel", 315 | "email": "me@mikevanriel.com" 316 | } 317 | ], 318 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 319 | "time": "2016-09-30 07:12:33" 320 | }, 321 | { 322 | "name": "phpdocumentor/type-resolver", 323 | "version": "0.2", 324 | "source": { 325 | "type": "git", 326 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 327 | "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" 328 | }, 329 | "dist": { 330 | "type": "zip", 331 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", 332 | "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", 333 | "shasum": "" 334 | }, 335 | "require": { 336 | "php": ">=5.5", 337 | "phpdocumentor/reflection-common": "^1.0" 338 | }, 339 | "require-dev": { 340 | "mockery/mockery": "^0.9.4", 341 | "phpunit/phpunit": "^5.2||^4.8.24" 342 | }, 343 | "type": "library", 344 | "extra": { 345 | "branch-alias": { 346 | "dev-master": "1.0.x-dev" 347 | } 348 | }, 349 | "autoload": { 350 | "psr-4": { 351 | "phpDocumentor\\Reflection\\": [ 352 | "src/" 353 | ] 354 | } 355 | }, 356 | "notification-url": "https://packagist.org/downloads/", 357 | "license": [ 358 | "MIT" 359 | ], 360 | "authors": [ 361 | { 362 | "name": "Mike van Riel", 363 | "email": "me@mikevanriel.com" 364 | } 365 | ], 366 | "time": "2016-06-10 07:14:17" 367 | }, 368 | { 369 | "name": "phpspec/prophecy", 370 | "version": "v1.6.1", 371 | "source": { 372 | "type": "git", 373 | "url": "https://github.com/phpspec/prophecy.git", 374 | "reference": "58a8137754bc24b25740d4281399a4a3596058e0" 375 | }, 376 | "dist": { 377 | "type": "zip", 378 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", 379 | "reference": "58a8137754bc24b25740d4281399a4a3596058e0", 380 | "shasum": "" 381 | }, 382 | "require": { 383 | "doctrine/instantiator": "^1.0.2", 384 | "php": "^5.3|^7.0", 385 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 386 | "sebastian/comparator": "^1.1", 387 | "sebastian/recursion-context": "^1.0" 388 | }, 389 | "require-dev": { 390 | "phpspec/phpspec": "^2.0" 391 | }, 392 | "type": "library", 393 | "extra": { 394 | "branch-alias": { 395 | "dev-master": "1.6.x-dev" 396 | } 397 | }, 398 | "autoload": { 399 | "psr-0": { 400 | "Prophecy\\": "src/" 401 | } 402 | }, 403 | "notification-url": "https://packagist.org/downloads/", 404 | "license": [ 405 | "MIT" 406 | ], 407 | "authors": [ 408 | { 409 | "name": "Konstantin Kudryashov", 410 | "email": "ever.zet@gmail.com", 411 | "homepage": "http://everzet.com" 412 | }, 413 | { 414 | "name": "Marcello Duarte", 415 | "email": "marcello.duarte@gmail.com" 416 | } 417 | ], 418 | "description": "Highly opinionated mocking framework for PHP 5.3+", 419 | "homepage": "https://github.com/phpspec/prophecy", 420 | "keywords": [ 421 | "Double", 422 | "Dummy", 423 | "fake", 424 | "mock", 425 | "spy", 426 | "stub" 427 | ], 428 | "time": "2016-06-07 08:13:47" 429 | }, 430 | { 431 | "name": "phpunit/php-code-coverage", 432 | "version": "2.2.4", 433 | "source": { 434 | "type": "git", 435 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 436 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 437 | }, 438 | "dist": { 439 | "type": "zip", 440 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 441 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 442 | "shasum": "" 443 | }, 444 | "require": { 445 | "php": ">=5.3.3", 446 | "phpunit/php-file-iterator": "~1.3", 447 | "phpunit/php-text-template": "~1.2", 448 | "phpunit/php-token-stream": "~1.3", 449 | "sebastian/environment": "^1.3.2", 450 | "sebastian/version": "~1.0" 451 | }, 452 | "require-dev": { 453 | "ext-xdebug": ">=2.1.4", 454 | "phpunit/phpunit": "~4" 455 | }, 456 | "suggest": { 457 | "ext-dom": "*", 458 | "ext-xdebug": ">=2.2.1", 459 | "ext-xmlwriter": "*" 460 | }, 461 | "type": "library", 462 | "extra": { 463 | "branch-alias": { 464 | "dev-master": "2.2.x-dev" 465 | } 466 | }, 467 | "autoload": { 468 | "classmap": [ 469 | "src/" 470 | ] 471 | }, 472 | "notification-url": "https://packagist.org/downloads/", 473 | "license": [ 474 | "BSD-3-Clause" 475 | ], 476 | "authors": [ 477 | { 478 | "name": "Sebastian Bergmann", 479 | "email": "sb@sebastian-bergmann.de", 480 | "role": "lead" 481 | } 482 | ], 483 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 484 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 485 | "keywords": [ 486 | "coverage", 487 | "testing", 488 | "xunit" 489 | ], 490 | "time": "2015-10-06 15:47:00" 491 | }, 492 | { 493 | "name": "phpunit/php-file-iterator", 494 | "version": "1.4.1", 495 | "source": { 496 | "type": "git", 497 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 498 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 499 | }, 500 | "dist": { 501 | "type": "zip", 502 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 503 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 504 | "shasum": "" 505 | }, 506 | "require": { 507 | "php": ">=5.3.3" 508 | }, 509 | "type": "library", 510 | "extra": { 511 | "branch-alias": { 512 | "dev-master": "1.4.x-dev" 513 | } 514 | }, 515 | "autoload": { 516 | "classmap": [ 517 | "src/" 518 | ] 519 | }, 520 | "notification-url": "https://packagist.org/downloads/", 521 | "license": [ 522 | "BSD-3-Clause" 523 | ], 524 | "authors": [ 525 | { 526 | "name": "Sebastian Bergmann", 527 | "email": "sb@sebastian-bergmann.de", 528 | "role": "lead" 529 | } 530 | ], 531 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 532 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 533 | "keywords": [ 534 | "filesystem", 535 | "iterator" 536 | ], 537 | "time": "2015-06-21 13:08:43" 538 | }, 539 | { 540 | "name": "phpunit/php-text-template", 541 | "version": "1.2.1", 542 | "source": { 543 | "type": "git", 544 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 545 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 546 | }, 547 | "dist": { 548 | "type": "zip", 549 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 550 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 551 | "shasum": "" 552 | }, 553 | "require": { 554 | "php": ">=5.3.3" 555 | }, 556 | "type": "library", 557 | "autoload": { 558 | "classmap": [ 559 | "src/" 560 | ] 561 | }, 562 | "notification-url": "https://packagist.org/downloads/", 563 | "license": [ 564 | "BSD-3-Clause" 565 | ], 566 | "authors": [ 567 | { 568 | "name": "Sebastian Bergmann", 569 | "email": "sebastian@phpunit.de", 570 | "role": "lead" 571 | } 572 | ], 573 | "description": "Simple template engine.", 574 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 575 | "keywords": [ 576 | "template" 577 | ], 578 | "time": "2015-06-21 13:50:34" 579 | }, 580 | { 581 | "name": "phpunit/php-timer", 582 | "version": "1.0.8", 583 | "source": { 584 | "type": "git", 585 | "url": "https://github.com/sebastianbergmann/php-timer.git", 586 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" 587 | }, 588 | "dist": { 589 | "type": "zip", 590 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", 591 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", 592 | "shasum": "" 593 | }, 594 | "require": { 595 | "php": ">=5.3.3" 596 | }, 597 | "require-dev": { 598 | "phpunit/phpunit": "~4|~5" 599 | }, 600 | "type": "library", 601 | "autoload": { 602 | "classmap": [ 603 | "src/" 604 | ] 605 | }, 606 | "notification-url": "https://packagist.org/downloads/", 607 | "license": [ 608 | "BSD-3-Clause" 609 | ], 610 | "authors": [ 611 | { 612 | "name": "Sebastian Bergmann", 613 | "email": "sb@sebastian-bergmann.de", 614 | "role": "lead" 615 | } 616 | ], 617 | "description": "Utility class for timing", 618 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 619 | "keywords": [ 620 | "timer" 621 | ], 622 | "time": "2016-05-12 18:03:57" 623 | }, 624 | { 625 | "name": "phpunit/php-token-stream", 626 | "version": "1.4.8", 627 | "source": { 628 | "type": "git", 629 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 630 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 631 | }, 632 | "dist": { 633 | "type": "zip", 634 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 635 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 636 | "shasum": "" 637 | }, 638 | "require": { 639 | "ext-tokenizer": "*", 640 | "php": ">=5.3.3" 641 | }, 642 | "require-dev": { 643 | "phpunit/phpunit": "~4.2" 644 | }, 645 | "type": "library", 646 | "extra": { 647 | "branch-alias": { 648 | "dev-master": "1.4-dev" 649 | } 650 | }, 651 | "autoload": { 652 | "classmap": [ 653 | "src/" 654 | ] 655 | }, 656 | "notification-url": "https://packagist.org/downloads/", 657 | "license": [ 658 | "BSD-3-Clause" 659 | ], 660 | "authors": [ 661 | { 662 | "name": "Sebastian Bergmann", 663 | "email": "sebastian@phpunit.de" 664 | } 665 | ], 666 | "description": "Wrapper around PHP's tokenizer extension.", 667 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 668 | "keywords": [ 669 | "tokenizer" 670 | ], 671 | "time": "2015-09-15 10:49:45" 672 | }, 673 | { 674 | "name": "phpunit/phpunit", 675 | "version": "4.8.27", 676 | "source": { 677 | "type": "git", 678 | "url": "https://github.com/sebastianbergmann/phpunit.git", 679 | "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90" 680 | }, 681 | "dist": { 682 | "type": "zip", 683 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c062dddcb68e44b563f66ee319ddae2b5a322a90", 684 | "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90", 685 | "shasum": "" 686 | }, 687 | "require": { 688 | "ext-dom": "*", 689 | "ext-json": "*", 690 | "ext-pcre": "*", 691 | "ext-reflection": "*", 692 | "ext-spl": "*", 693 | "php": ">=5.3.3", 694 | "phpspec/prophecy": "^1.3.1", 695 | "phpunit/php-code-coverage": "~2.1", 696 | "phpunit/php-file-iterator": "~1.4", 697 | "phpunit/php-text-template": "~1.2", 698 | "phpunit/php-timer": "^1.0.6", 699 | "phpunit/phpunit-mock-objects": "~2.3", 700 | "sebastian/comparator": "~1.1", 701 | "sebastian/diff": "~1.2", 702 | "sebastian/environment": "~1.3", 703 | "sebastian/exporter": "~1.2", 704 | "sebastian/global-state": "~1.0", 705 | "sebastian/version": "~1.0", 706 | "symfony/yaml": "~2.1|~3.0" 707 | }, 708 | "suggest": { 709 | "phpunit/php-invoker": "~1.1" 710 | }, 711 | "bin": [ 712 | "phpunit" 713 | ], 714 | "type": "library", 715 | "extra": { 716 | "branch-alias": { 717 | "dev-master": "4.8.x-dev" 718 | } 719 | }, 720 | "autoload": { 721 | "classmap": [ 722 | "src/" 723 | ] 724 | }, 725 | "notification-url": "https://packagist.org/downloads/", 726 | "license": [ 727 | "BSD-3-Clause" 728 | ], 729 | "authors": [ 730 | { 731 | "name": "Sebastian Bergmann", 732 | "email": "sebastian@phpunit.de", 733 | "role": "lead" 734 | } 735 | ], 736 | "description": "The PHP Unit Testing framework.", 737 | "homepage": "https://phpunit.de/", 738 | "keywords": [ 739 | "phpunit", 740 | "testing", 741 | "xunit" 742 | ], 743 | "time": "2016-07-21 06:48:14" 744 | }, 745 | { 746 | "name": "phpunit/phpunit-mock-objects", 747 | "version": "2.3.8", 748 | "source": { 749 | "type": "git", 750 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 751 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 752 | }, 753 | "dist": { 754 | "type": "zip", 755 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 756 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 757 | "shasum": "" 758 | }, 759 | "require": { 760 | "doctrine/instantiator": "^1.0.2", 761 | "php": ">=5.3.3", 762 | "phpunit/php-text-template": "~1.2", 763 | "sebastian/exporter": "~1.2" 764 | }, 765 | "require-dev": { 766 | "phpunit/phpunit": "~4.4" 767 | }, 768 | "suggest": { 769 | "ext-soap": "*" 770 | }, 771 | "type": "library", 772 | "extra": { 773 | "branch-alias": { 774 | "dev-master": "2.3.x-dev" 775 | } 776 | }, 777 | "autoload": { 778 | "classmap": [ 779 | "src/" 780 | ] 781 | }, 782 | "notification-url": "https://packagist.org/downloads/", 783 | "license": [ 784 | "BSD-3-Clause" 785 | ], 786 | "authors": [ 787 | { 788 | "name": "Sebastian Bergmann", 789 | "email": "sb@sebastian-bergmann.de", 790 | "role": "lead" 791 | } 792 | ], 793 | "description": "Mock Object library for PHPUnit", 794 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 795 | "keywords": [ 796 | "mock", 797 | "xunit" 798 | ], 799 | "time": "2015-10-02 06:51:40" 800 | }, 801 | { 802 | "name": "sebastian/comparator", 803 | "version": "1.2.0", 804 | "source": { 805 | "type": "git", 806 | "url": "https://github.com/sebastianbergmann/comparator.git", 807 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 808 | }, 809 | "dist": { 810 | "type": "zip", 811 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 812 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 813 | "shasum": "" 814 | }, 815 | "require": { 816 | "php": ">=5.3.3", 817 | "sebastian/diff": "~1.2", 818 | "sebastian/exporter": "~1.2" 819 | }, 820 | "require-dev": { 821 | "phpunit/phpunit": "~4.4" 822 | }, 823 | "type": "library", 824 | "extra": { 825 | "branch-alias": { 826 | "dev-master": "1.2.x-dev" 827 | } 828 | }, 829 | "autoload": { 830 | "classmap": [ 831 | "src/" 832 | ] 833 | }, 834 | "notification-url": "https://packagist.org/downloads/", 835 | "license": [ 836 | "BSD-3-Clause" 837 | ], 838 | "authors": [ 839 | { 840 | "name": "Jeff Welch", 841 | "email": "whatthejeff@gmail.com" 842 | }, 843 | { 844 | "name": "Volker Dusch", 845 | "email": "github@wallbash.com" 846 | }, 847 | { 848 | "name": "Bernhard Schussek", 849 | "email": "bschussek@2bepublished.at" 850 | }, 851 | { 852 | "name": "Sebastian Bergmann", 853 | "email": "sebastian@phpunit.de" 854 | } 855 | ], 856 | "description": "Provides the functionality to compare PHP values for equality", 857 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 858 | "keywords": [ 859 | "comparator", 860 | "compare", 861 | "equality" 862 | ], 863 | "time": "2015-07-26 15:48:44" 864 | }, 865 | { 866 | "name": "sebastian/diff", 867 | "version": "1.4.1", 868 | "source": { 869 | "type": "git", 870 | "url": "https://github.com/sebastianbergmann/diff.git", 871 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 872 | }, 873 | "dist": { 874 | "type": "zip", 875 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 876 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 877 | "shasum": "" 878 | }, 879 | "require": { 880 | "php": ">=5.3.3" 881 | }, 882 | "require-dev": { 883 | "phpunit/phpunit": "~4.8" 884 | }, 885 | "type": "library", 886 | "extra": { 887 | "branch-alias": { 888 | "dev-master": "1.4-dev" 889 | } 890 | }, 891 | "autoload": { 892 | "classmap": [ 893 | "src/" 894 | ] 895 | }, 896 | "notification-url": "https://packagist.org/downloads/", 897 | "license": [ 898 | "BSD-3-Clause" 899 | ], 900 | "authors": [ 901 | { 902 | "name": "Kore Nordmann", 903 | "email": "mail@kore-nordmann.de" 904 | }, 905 | { 906 | "name": "Sebastian Bergmann", 907 | "email": "sebastian@phpunit.de" 908 | } 909 | ], 910 | "description": "Diff implementation", 911 | "homepage": "https://github.com/sebastianbergmann/diff", 912 | "keywords": [ 913 | "diff" 914 | ], 915 | "time": "2015-12-08 07:14:41" 916 | }, 917 | { 918 | "name": "sebastian/environment", 919 | "version": "1.3.8", 920 | "source": { 921 | "type": "git", 922 | "url": "https://github.com/sebastianbergmann/environment.git", 923 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" 924 | }, 925 | "dist": { 926 | "type": "zip", 927 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", 928 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", 929 | "shasum": "" 930 | }, 931 | "require": { 932 | "php": "^5.3.3 || ^7.0" 933 | }, 934 | "require-dev": { 935 | "phpunit/phpunit": "^4.8 || ^5.0" 936 | }, 937 | "type": "library", 938 | "extra": { 939 | "branch-alias": { 940 | "dev-master": "1.3.x-dev" 941 | } 942 | }, 943 | "autoload": { 944 | "classmap": [ 945 | "src/" 946 | ] 947 | }, 948 | "notification-url": "https://packagist.org/downloads/", 949 | "license": [ 950 | "BSD-3-Clause" 951 | ], 952 | "authors": [ 953 | { 954 | "name": "Sebastian Bergmann", 955 | "email": "sebastian@phpunit.de" 956 | } 957 | ], 958 | "description": "Provides functionality to handle HHVM/PHP environments", 959 | "homepage": "http://www.github.com/sebastianbergmann/environment", 960 | "keywords": [ 961 | "Xdebug", 962 | "environment", 963 | "hhvm" 964 | ], 965 | "time": "2016-08-18 05:49:44" 966 | }, 967 | { 968 | "name": "sebastian/exporter", 969 | "version": "1.2.2", 970 | "source": { 971 | "type": "git", 972 | "url": "https://github.com/sebastianbergmann/exporter.git", 973 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" 974 | }, 975 | "dist": { 976 | "type": "zip", 977 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", 978 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", 979 | "shasum": "" 980 | }, 981 | "require": { 982 | "php": ">=5.3.3", 983 | "sebastian/recursion-context": "~1.0" 984 | }, 985 | "require-dev": { 986 | "ext-mbstring": "*", 987 | "phpunit/phpunit": "~4.4" 988 | }, 989 | "type": "library", 990 | "extra": { 991 | "branch-alias": { 992 | "dev-master": "1.3.x-dev" 993 | } 994 | }, 995 | "autoload": { 996 | "classmap": [ 997 | "src/" 998 | ] 999 | }, 1000 | "notification-url": "https://packagist.org/downloads/", 1001 | "license": [ 1002 | "BSD-3-Clause" 1003 | ], 1004 | "authors": [ 1005 | { 1006 | "name": "Jeff Welch", 1007 | "email": "whatthejeff@gmail.com" 1008 | }, 1009 | { 1010 | "name": "Volker Dusch", 1011 | "email": "github@wallbash.com" 1012 | }, 1013 | { 1014 | "name": "Bernhard Schussek", 1015 | "email": "bschussek@2bepublished.at" 1016 | }, 1017 | { 1018 | "name": "Sebastian Bergmann", 1019 | "email": "sebastian@phpunit.de" 1020 | }, 1021 | { 1022 | "name": "Adam Harvey", 1023 | "email": "aharvey@php.net" 1024 | } 1025 | ], 1026 | "description": "Provides the functionality to export PHP variables for visualization", 1027 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1028 | "keywords": [ 1029 | "export", 1030 | "exporter" 1031 | ], 1032 | "time": "2016-06-17 09:04:28" 1033 | }, 1034 | { 1035 | "name": "sebastian/global-state", 1036 | "version": "1.1.1", 1037 | "source": { 1038 | "type": "git", 1039 | "url": "https://github.com/sebastianbergmann/global-state.git", 1040 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1041 | }, 1042 | "dist": { 1043 | "type": "zip", 1044 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1045 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1046 | "shasum": "" 1047 | }, 1048 | "require": { 1049 | "php": ">=5.3.3" 1050 | }, 1051 | "require-dev": { 1052 | "phpunit/phpunit": "~4.2" 1053 | }, 1054 | "suggest": { 1055 | "ext-uopz": "*" 1056 | }, 1057 | "type": "library", 1058 | "extra": { 1059 | "branch-alias": { 1060 | "dev-master": "1.0-dev" 1061 | } 1062 | }, 1063 | "autoload": { 1064 | "classmap": [ 1065 | "src/" 1066 | ] 1067 | }, 1068 | "notification-url": "https://packagist.org/downloads/", 1069 | "license": [ 1070 | "BSD-3-Clause" 1071 | ], 1072 | "authors": [ 1073 | { 1074 | "name": "Sebastian Bergmann", 1075 | "email": "sebastian@phpunit.de" 1076 | } 1077 | ], 1078 | "description": "Snapshotting of global state", 1079 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1080 | "keywords": [ 1081 | "global state" 1082 | ], 1083 | "time": "2015-10-12 03:26:01" 1084 | }, 1085 | { 1086 | "name": "sebastian/recursion-context", 1087 | "version": "1.0.2", 1088 | "source": { 1089 | "type": "git", 1090 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1091 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791" 1092 | }, 1093 | "dist": { 1094 | "type": "zip", 1095 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", 1096 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791", 1097 | "shasum": "" 1098 | }, 1099 | "require": { 1100 | "php": ">=5.3.3" 1101 | }, 1102 | "require-dev": { 1103 | "phpunit/phpunit": "~4.4" 1104 | }, 1105 | "type": "library", 1106 | "extra": { 1107 | "branch-alias": { 1108 | "dev-master": "1.0.x-dev" 1109 | } 1110 | }, 1111 | "autoload": { 1112 | "classmap": [ 1113 | "src/" 1114 | ] 1115 | }, 1116 | "notification-url": "https://packagist.org/downloads/", 1117 | "license": [ 1118 | "BSD-3-Clause" 1119 | ], 1120 | "authors": [ 1121 | { 1122 | "name": "Jeff Welch", 1123 | "email": "whatthejeff@gmail.com" 1124 | }, 1125 | { 1126 | "name": "Sebastian Bergmann", 1127 | "email": "sebastian@phpunit.de" 1128 | }, 1129 | { 1130 | "name": "Adam Harvey", 1131 | "email": "aharvey@php.net" 1132 | } 1133 | ], 1134 | "description": "Provides functionality to recursively process PHP variables", 1135 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1136 | "time": "2015-11-11 19:50:13" 1137 | }, 1138 | { 1139 | "name": "sebastian/version", 1140 | "version": "1.0.6", 1141 | "source": { 1142 | "type": "git", 1143 | "url": "https://github.com/sebastianbergmann/version.git", 1144 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1145 | }, 1146 | "dist": { 1147 | "type": "zip", 1148 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1149 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1150 | "shasum": "" 1151 | }, 1152 | "type": "library", 1153 | "autoload": { 1154 | "classmap": [ 1155 | "src/" 1156 | ] 1157 | }, 1158 | "notification-url": "https://packagist.org/downloads/", 1159 | "license": [ 1160 | "BSD-3-Clause" 1161 | ], 1162 | "authors": [ 1163 | { 1164 | "name": "Sebastian Bergmann", 1165 | "email": "sebastian@phpunit.de", 1166 | "role": "lead" 1167 | } 1168 | ], 1169 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1170 | "homepage": "https://github.com/sebastianbergmann/version", 1171 | "time": "2015-06-21 13:59:46" 1172 | }, 1173 | { 1174 | "name": "symfony/yaml", 1175 | "version": "v3.1.5", 1176 | "source": { 1177 | "type": "git", 1178 | "url": "https://github.com/symfony/yaml.git", 1179 | "reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3" 1180 | }, 1181 | "dist": { 1182 | "type": "zip", 1183 | "url": "https://api.github.com/repos/symfony/yaml/zipball/368b9738d4033c8b93454cb0dbd45d305135a6d3", 1184 | "reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3", 1185 | "shasum": "" 1186 | }, 1187 | "require": { 1188 | "php": ">=5.5.9" 1189 | }, 1190 | "type": "library", 1191 | "extra": { 1192 | "branch-alias": { 1193 | "dev-master": "3.1-dev" 1194 | } 1195 | }, 1196 | "autoload": { 1197 | "psr-4": { 1198 | "Symfony\\Component\\Yaml\\": "" 1199 | }, 1200 | "exclude-from-classmap": [ 1201 | "/Tests/" 1202 | ] 1203 | }, 1204 | "notification-url": "https://packagist.org/downloads/", 1205 | "license": [ 1206 | "MIT" 1207 | ], 1208 | "authors": [ 1209 | { 1210 | "name": "Fabien Potencier", 1211 | "email": "fabien@symfony.com" 1212 | }, 1213 | { 1214 | "name": "Symfony Community", 1215 | "homepage": "https://symfony.com/contributors" 1216 | } 1217 | ], 1218 | "description": "Symfony Yaml Component", 1219 | "homepage": "https://symfony.com", 1220 | "time": "2016-09-25 08:27:07" 1221 | }, 1222 | { 1223 | "name": "webmozart/assert", 1224 | "version": "1.1.0", 1225 | "source": { 1226 | "type": "git", 1227 | "url": "https://github.com/webmozart/assert.git", 1228 | "reference": "bb2d123231c095735130cc8f6d31385a44c7b308" 1229 | }, 1230 | "dist": { 1231 | "type": "zip", 1232 | "url": "https://api.github.com/repos/webmozart/assert/zipball/bb2d123231c095735130cc8f6d31385a44c7b308", 1233 | "reference": "bb2d123231c095735130cc8f6d31385a44c7b308", 1234 | "shasum": "" 1235 | }, 1236 | "require": { 1237 | "php": "^5.3.3|^7.0" 1238 | }, 1239 | "require-dev": { 1240 | "phpunit/phpunit": "^4.6", 1241 | "sebastian/version": "^1.0.1" 1242 | }, 1243 | "type": "library", 1244 | "extra": { 1245 | "branch-alias": { 1246 | "dev-master": "1.2-dev" 1247 | } 1248 | }, 1249 | "autoload": { 1250 | "psr-4": { 1251 | "Webmozart\\Assert\\": "src/" 1252 | } 1253 | }, 1254 | "notification-url": "https://packagist.org/downloads/", 1255 | "license": [ 1256 | "MIT" 1257 | ], 1258 | "authors": [ 1259 | { 1260 | "name": "Bernhard Schussek", 1261 | "email": "bschussek@gmail.com" 1262 | } 1263 | ], 1264 | "description": "Assertions to validate method input/output with nice error messages.", 1265 | "keywords": [ 1266 | "assert", 1267 | "check", 1268 | "validate" 1269 | ], 1270 | "time": "2016-08-09 15:02:57" 1271 | } 1272 | ], 1273 | "aliases": [], 1274 | "minimum-stability": "stable", 1275 | "stability-flags": [], 1276 | "prefer-stable": false, 1277 | "prefer-lowest": false, 1278 | "platform": { 1279 | "php": ">=5.5.9" 1280 | }, 1281 | "platform-dev": [] 1282 | } 1283 | --------------------------------------------------------------------------------