<?php /** * Created by PhpStorm. * User: Zero * Date: 2020/8/18 * Time: 8:18 */ namespace Meibuyu\Micro\Shopify\lib; use Exception; use Meibuyu\Micro\Shopify\tools\CurlHttpRequestJson; use Meibuyu\Micro\Shopify\tools\HttpRequestJson; use Psr\Http\Message\ResponseInterface; abstract class AbstractShopify { /** * 资源id * @var int */ public $id = null; protected $httpHeaders = []; protected $resourceUrl; protected $resourceKey; protected $pluralizeKey; /** * 无count方法 * @var boolean */ public $countEnabled = true; // 子集资源 protected $childResource = []; // 自定义请求方法 protected $customGetActions = []; protected $customPostActions = []; protected $customPutActions = []; protected $customDeleteActions = []; private $config; /** * @var HttpRequestJson|CurlHttpRequestJson */ private $httpRequestJson; /** * AbstractShopify constructor. * @param $config * @param null $id * @param string $parentResourceUrl * @throws Exception */ public function __construct($config, $id = null, $parentResourceUrl = null) { $this->config = $config; $this->id = $id; $this->pluralizeKey = $this->pluralizeKey ?: $this->resourceKey . 's'; $this->resourceUrl = ($parentResourceUrl ? "$parentResourceUrl/" : $config['api_url']) . $this->pluralizeKey . ($this->id ? "/{$this->id}" : ''); $this->httpRequestJson = make(CurlHttpRequestJson::class); if (isset($config['is_private_app']) && $config['is_private_app'] == false) { // 如果不是私人应用,则使用访问令牌 if (isset($config['access_token'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['access_token']; } elseif (!isset($config['access_token'])) { throw new Exception("请设置access_token值"); } } else { if (isset($config['api_password'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['api_password']; } elseif (!isset($config['api_password'])) { throw new Exception("请设置api_password值"); } } } /** * 调用子集资源 * @param $childName * @return mixed */ public function __get($childName) { return $this->$childName(); } /** * 调用自定义方法 * @param $name * @param $arguments * @return mixed * @throws Exception */ public function __call($name, $arguments) { if (ctype_upper($name[0])) { $childKey = array_search($name, $this->childResource); if ($childKey === false) { throw new Exception(" $name 不属于 {$this->getResourceName()}"); } $childClassName = !is_numeric($childKey) ? $childKey : $name; $childClass = __NAMESPACE__ . "\\" . $childClassName; $resourceID = !empty($arguments) ? $arguments[0] : null; return new $childClass($this->config, $resourceID, $this->resourceUrl); } else { $actionMaps = [ 'post' => 'customPostActions', 'put' => 'customPutActions', 'get' => 'customGetActions', 'delete' => 'customDeleteActions', ]; //Get the array key for the action in the actions array foreach ($actionMaps as $httpMethod => $actionArrayKey) { $actionKey = array_search($name, $this->$actionArrayKey); if ($actionKey !== false) break; } if ($actionKey === false) { throw new Exception("No action named $name is defined for " . $this->getResourceName()); } //If any associative key is given to the action, then it will be considered as the method name, //otherwise the action name will be the method name $customAction = !is_numeric($actionKey) ? $actionKey : $name; //Get the first argument if provided with the method call $methodArgument = !empty($arguments) ? $arguments[0] : []; //Url parameters $urlParams = []; //Data body $dataArray = []; //Consider the argument as url parameters for get and delete request //and data array for post and put request if ($httpMethod == 'post' || $httpMethod == 'put') { $dataArray = $methodArgument; } else { $urlParams = $methodArgument; } $url = $this->generateUrl($urlParams, null, $customAction); if ($httpMethod == 'post' || $httpMethod == 'put') { return $this->$httpMethod($dataArray, $url, false); } else { return $this->$httpMethod($dataArray, $url); } } } private function getResourceName() { return substr(get_called_class(), strrpos(get_called_class(), '\\') + 1); } public function generateUrl($urlParams = [], $id = null, $customAction = null) { $resourceUrl = $this->resourceUrl; if ($id) { if ($this->id) { if ($id !== $this->id) { $resourceUrl = str_replace($this->id, $id, $this->resourceUrl); } } else { $resourceUrl = $this->resourceUrl . "/$id"; } } return $resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } /** * 获取数量 * @param array $urlParams * @return mixed * @throws Exception */ public function count($urlParams = []) { if (!$this->countEnabled) { throw new Exception("当前类{$this->getResourceName()}不支持count()方法"); } $url = $this->generateUrl($urlParams, null, 'count'); return $this->get([], $url, 'count'); } /** * @param array $urlParams * @param null $url * @param null $dataKey * @return mixed * @throws Exception */ public function get($urlParams = [], $url = null, $dataKey = null) { if (!$url) $url = $this->generateUrl($urlParams); $response = $this->httpRequestJson->get($url, $this->httpHeaders); if (!$dataKey) $dataKey = $this->id ? $this->resourceKey : $this->pluralizeKey; return $this->processResponse($response, $dataKey); } /** * 分页 * @param null $url * @param array $urlParams * @return mixed ['data' => [数据], 'next_link' => '下一页链接'] * @throws Exception */ public function page($url = null, $urlParams = []) { if (!$url) $url = $this->generateUrl($urlParams); $response = $this->httpRequestJson->get($url, $this->httpHeaders); return $this->processPageResponse($response, $this->pluralizeKey); } /** * 根据id获取一条数据 * @param $id * @param array $urlParams * @return mixed * @throws Exception */ public function show($id, $urlParams = []) { $url = $this->generateUrl($urlParams, $id); $response = $this->httpRequestJson->get($url, $this->httpHeaders); return $this->processResponse($response, $this->resourceKey); } /** * @param $dataArray * @param null $url * @param bool $wrapData * @return mixed * @throws Exception */ public function post($dataArray, $url = null, $wrapData = true) { if (!$url) $url = $this->generateUrl(); if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); $response = $this->httpRequestJson->post($url, $dataArray, $this->httpHeaders); return $this->processResponse($response, $this->resourceKey); } /** * @param int|string $id * @param $dataArray * @param null $url * @param bool $wrapData * @return mixed * @throws Exception */ public function put($id, $dataArray, $url = null, $wrapData = true) { if (!$url) $url = $this->generateUrl([], $id); if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); $response = $this->httpRequestJson->put($url, $dataArray, $this->httpHeaders); return $this->processResponse($response, $this->resourceKey); } /** * @param int|string $id * @param array $urlParams * @param null $url * @return mixed * @throws Exception */ public function delete($id = null, $urlParams = [], $url = null) { if (!$url) $url = $this->generateUrl($urlParams, $id); $response = $this->httpRequestJson->delete($url, $this->httpHeaders); return $this->processResponse($response); } protected function wrapData($dataArray, $dataKey = null) { if (!$dataKey) $dataKey = $this->resourceKey; return [$dataKey => $dataArray]; } protected function castString($array) { if (!is_array($array)) return (string)$array; $string = ''; $i = 0; foreach ($array as $key => $val) { $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; $i++; } $string = rtrim($string, ', '); return $string; } /** * 处理响应 * @param array $response * @param null $dataKey * @return mixed * @throws Exception */ public function processResponse($response, $dataKey = null) { [$code, , $content] = $response; $content = json_decode($content, true); if (isset($content['errors'])) { throw new Exception($this->castString($content['errors']), $code); } if ($dataKey && isset($content[$dataKey])) { return $content[$dataKey]; } else { return $content; } } /** * 处理响应 * @param ResponseInterface $response * @param null $dataKey * @return mixed * @throws Exception */ public function processPageResponse($response, $dataKey = null) { [$code, $headers, $content] = $response; $content = json_decode($content, true); $link = $this->getLink($headers); if (isset($content['errors'])) { throw new Exception($this->castString($content['errors']), $code); } if ($dataKey && isset($content[$dataKey])) { $data = $content[$dataKey]; } else { $data = $content; } return ['data' => $data, 'next_link' => $link]; } public function getLink($header, $type = 'next') { if (!empty($header['x-shopify-api-version'][0]) && $header['x-shopify-api-version'][0] < '2019-07') { return null; } if (!empty($header['link'][0])) { $headerLinks = $header['link'][0]; if (stristr($headerLinks, '; rel="' . $type . '"') > -1) { $headerLinks = explode(',', $headerLinks); foreach ($headerLinks as $headerLink) { if (stristr($headerLink, '; rel="' . $type . '"') === -1) { continue; } $pattern = '#<(.*?)>; rel="' . $type . '"#m'; preg_match($pattern, $headerLink, $linkResponseHeaders); if ($linkResponseHeaders) { return $linkResponseHeaders[1]; } } } } return null; } }