vendor/storyblok/php-client/src/Storyblok/Client.php line 313

Open in your IDE?
  1. <?php
  2. namespace Storyblok;
  3. use GuzzleHttp\Client as Guzzle;
  4. use Apix\Cache as ApixCache;
  5. use GuzzleHttp\RequestOptions;
  6. /**
  7. * Storyblok Client
  8. */
  9. class Client extends BaseClient
  10. {
  11.     const CACHE_VERSION_KEY "storyblok:cache_version";
  12.     const EXCEPTION_GENERIC_HTTP_ERROR "An HTTP Error has occurred!";
  13.     /**
  14.      * @var string
  15.      */
  16.     public $cacheVersion;
  17.     /**
  18.      * @var string
  19.      */
  20.     private $linksPath 'links/';
  21.     /**
  22.      * @var boolean
  23.      */
  24.     private $editModeEnabled;
  25.     /**
  26.      * @var string
  27.      */
  28.     private $resolveRelations;
  29.     /**
  30.      * @var string
  31.      */
  32.     private $resolveLinks;
  33.     /**
  34.      * @var boolean
  35.      */
  36.     private $cacheNotFound;
  37.     /**
  38.      * @var Cache
  39.      */
  40.     protected $cache;
  41.     /**
  42.      * @param string $apiKey
  43.      * @param string $apiEndpoint
  44.      * @param string $apiVersion
  45.      * @param bool   $ssl
  46.      */
  47.     function __construct($apiKey null$apiEndpoint "api.storyblok.com"$apiVersion "v1"$ssl false)
  48.     {
  49.         parent::__construct($apiKey$apiEndpoint$apiVersion$ssl);
  50.         if (isset($_GET['_storyblok'])) {
  51.             $this->editModeEnabled $_GET['_storyblok'];
  52.         } else {
  53.             $this->editModeEnabled false;
  54.         }
  55.     }
  56.     /**
  57.      * Enables editmode to receive draft versions
  58.      *
  59.      * @param  boolean $enabled
  60.      * @return Client
  61.      */
  62.     public function editMode($enabled true)
  63.     {
  64.         $this->editModeEnabled $enabled;
  65.         return $this;
  66.     }
  67.     /**
  68.      * Enables caching for 404 responses
  69.      *
  70.      * @param  boolean $enabled
  71.      * @return Client
  72.      */
  73.     public function cacheNotFound($enabled true)
  74.     {
  75.         $this->cacheNotFound $enabled;
  76.         return $this;
  77.     }
  78.     /**
  79.      * Set cache driver and optional the cache path
  80.      *
  81.      * @param string $driver Driver
  82.      * @param array $options Path for file cache
  83.      * @return \Storyblok\Client
  84.      */
  85.     public function setCache($driver$options = array())
  86.     {
  87.         $options['serializer'] = 'php';
  88.         $options['prefix_key'] = 'storyblok:';
  89.         $options['prefix_tag'] = 'storyblok:';
  90.         switch ($driver) {
  91.             case 'mysql':
  92.                 $dbh $options['pdo'];
  93.                 $this->cache = new ApixCache\Pdo\Mysql($dbh$options);
  94.                 break;
  95.             case 'sqlite':
  96.                 $dbh $options['pdo'];
  97.                 $this->cache = new ApixCache\Pdo\Sqlite($dbh$options);
  98.                 break;
  99.             case 'postgres':
  100.                 $dbh $options['pdo'];
  101.                 $this->cache = new ApixCache\Pdo\Pgsql($dbh$options);
  102.                 break;
  103.             default:
  104.                 $options['directory'] = $options['path'];
  105.                 $this->cache = new ApixCache\Files($options);
  106.                 break;
  107.         }
  108.         $this->cacheVersion $this->cache->load(self::CACHE_VERSION_KEY);
  109.         if (!$this->cacheVersion) {
  110.             $this->setCacheVersion();
  111.         }
  112.         return $this;
  113.     }
  114.     /**
  115.      * Manually delete the cache of one item
  116.      *
  117.      * @param  string $slug Slug
  118.      * @return \Storyblok\Client
  119.      */
  120.     public function deleteCacheBySlug($slug)
  121.     {
  122.         $key $this->_getCacheKey('stories/' $slug);
  123.         if ($this->cache) {
  124.             $this->cache->delete($key);
  125.             // Always refresh cache of links
  126.             $this->cache->delete($this->linksPath);
  127.             $this->setCacheVersion();
  128.         }
  129.         return $this;
  130.     }
  131.     /**
  132.      * Flush all cache
  133.      *
  134.      * @return \Storyblok\Client
  135.      */
  136.     public function flushCache()
  137.     {
  138.         if ($this->cache) {
  139.             $this->cache->flush();
  140.             $this->setCacheVersion();
  141.         }
  142.         return $this;
  143.     }
  144.     /**
  145.      * Automatically delete the cache of one item if client sends published parameter
  146.      *
  147.      * @param  string $key Cache key
  148.      * @return \Storyblok\Client
  149.      */
  150.     private function reCacheOnPublish($key)
  151.     {
  152.         if (isset($_GET['_storyblok_published']) && $this->cache) {
  153.             $this->cache->delete($key);
  154.             // Always refresh cache of links
  155.             $this->cache->delete($this->linksPath);
  156.             $this->setCacheVersion();
  157.         }
  158.         return $this;
  159.     }
  160.     /**
  161.      * Sets cache version to get a fresh version from cdn after clearing the cache
  162.      *
  163.      * @return \Storyblok\Client
  164.      */
  165.     public function setCacheVersion()
  166.     {
  167.         if ($this->cache) {
  168.             $timestamp time();
  169.             $this->cache->save($timestampself::CACHE_VERSION_KEY);
  170.             $this->cacheVersion $timestamp;
  171.         }
  172.         return $this;
  173.     }
  174.     /**
  175.      * Gets cache version from cache or as timestamp
  176.      *
  177.      * @return Integer
  178.      */
  179.     function getCacheVersion()
  180.     {
  181.         if (empty($this->cacheVersion)) {
  182.             return time();
  183.         } else {
  184.             return $this->cacheVersion;
  185.         }
  186.     }
  187.     /**
  188.      * Gets a story by the slug identifier
  189.      *
  190.      * @param  string $slug Slug
  191.      *
  192.      * @return Client
  193.      * @throws ApiException
  194.      */
  195.     public function getStoryBySlug($slug)
  196.     {
  197.         return $this->getStory($slug);
  198.     }
  199.     /**
  200.      * Gets a story by it’s UUID
  201.      *
  202.      * @param string $uuid UUID
  203.      *
  204.      * @return Client
  205.      * @throws ApiException
  206.      */
  207.     public function getStoryByUuid($uuid)
  208.     {
  209.         return $this->getStory($uuidtrue);
  210.     }
  211.     /**
  212.      * Gets a story
  213.      *
  214.      * @param  string $slug Slug
  215.      * @param bool $byUuid
  216.      *
  217.      * @return Client
  218.      * @throws ApiException
  219.      */
  220.     private function getStory($slug$byUuid false)
  221.     {
  222.         $version 'published';
  223.         if ($this->editModeEnabled) {
  224.             $version 'draft';
  225.         }
  226.         $key 'stories/' $slug;
  227.         $cachekey $this->_getCacheKey($key);
  228.         $this->reCacheOnPublish($key);
  229.         if ($version == 'published' && $this->cache && $cachedItem $this->cache->load($cachekey)) {
  230.             if ($this->cacheNotFound && $cachedItem->httpResponseCode == 404) {
  231.                 throw new ApiException(self::EXCEPTION_GENERIC_HTTP_ERROR404);
  232.             }
  233.             $this->_assignState($cachedItem);
  234.         } else {
  235.             $options = array(
  236.                 'token' => $this->getApiKey(),
  237.                 'version' => $version,
  238.                 'cache_version' => $this->getCacheVersion()
  239.             );
  240.             if ($byUuid) {
  241.                 $options['find_by'] = 'uuid';
  242.             }
  243.             if ($this->resolveRelations) {
  244.                 $options['resolve_relations'] = $this->resolveRelations;
  245.             }
  246.             if ($this->resolveLinks) {
  247.                 $options['resolve_links'] = $this->resolveLinks;
  248.             }
  249.             try {
  250.                 $response $this->get($key$options);
  251.                 $this->_save($response$cachekey$version);
  252.             } catch (\Exception $e) {
  253.                 if ($this->cacheNotFound && $e->getCode() === 404) {
  254.                     $result = new \stdClass();
  255.                     $result->httpResponseBody = [];
  256.                     $result->httpResponseCode 404;
  257.                     $result->httpResponseHeaders = [];
  258.                     $this->cache->save($result$cachekey);
  259.                 }
  260.                 throw new ApiException(self::EXCEPTION_GENERIC_HTTP_ERROR ' - ' $e->getMessage(), $e->getCode());
  261.             }
  262.         }
  263.         return $this;
  264.     }
  265.     /**
  266.      * Gets a list of stories
  267.      *
  268.      * array(
  269.      *    'starts_with' => $slug,
  270.      *    'with_tag' => $tag,
  271.      *    'sort_by' => $sort_by,
  272.      *    'per_page' => 25,
  273.      *    'page' => 0
  274.      * )
  275.      *
  276.      *
  277.      * @param  array $options Options
  278.      * @return \Storyblok\Client
  279.      */
  280.     public function getStories($options = array())
  281.     {
  282.         $version 'published';
  283.         $endpointUrl 'stories/';
  284.         if ($this->editModeEnabled) {
  285.             $version 'draft';
  286.         }
  287.         $key 'stories/' serialize($this->_prepareOptionsForKey($options));
  288.         $cachekey $this->_getCacheKey($key);
  289.         $this->reCacheOnPublish($key);
  290.         if ($version == 'published' && $this->cache && $cachedItem $this->cache->load($cachekey)) {
  291.             $this->_assignState($cachedItem);
  292.         } else {
  293.             $options array_merge($options, array(
  294.                 'token' => $this->getApiKey(),
  295.                 'version' => $version,
  296.                 'cache_version' => $this->getCacheVersion()
  297.             ));
  298.             if ($this->resolveRelations) {
  299.                 $options['resolve_relations'] = $this->resolveRelations;
  300.             }
  301.             $response $this->get($endpointUrl$options);
  302.             $this->_save($response$cachekey$version);
  303.         }
  304.         return $this;
  305.     }
  306.     /**
  307.      *  Sets global reference.
  308.      *
  309.      *  eg. global.global_referece
  310.      *
  311.      * @param $reference
  312.      * @return $this
  313.      */
  314.     public function resolveRelations($reference)
  315.     {
  316.         $this->resolveRelations $reference;
  317.         return $this;
  318.     }
  319.     /**
  320.      * Set reference for how to resolve links.
  321.      *
  322.      * @param $reference
  323.      * @return $this
  324.      */
  325.     public function resolveLinks($reference)
  326.     {
  327.         $this->resolveLinks $reference;
  328.         return $this;
  329.     }
  330.     /**
  331.      * Gets a list of tags
  332.      *
  333.      * array(
  334.      *    'starts_with' => $slug
  335.      * )
  336.      *
  337.      *
  338.      * @param  array $options Options
  339.      * @return \Storyblok\Client
  340.      */
  341.     public function getTags($options = array())
  342.     {
  343.         $version 'published';
  344.         $endpointUrl 'tags/';
  345.         if ($this->editModeEnabled) {
  346.             $version 'draft';
  347.         }
  348.         $key 'tags/' serialize($options);
  349.         $cachekey $this->_getCacheKey($key);
  350.         $this->reCacheOnPublish($key);
  351.         if ($version == 'published' && $this->cache && $cachedItem $this->cache->load($cachekey)) {
  352.             $this->_assignState($cachedItem);
  353.         } else {
  354.             $options array_merge($options, array(
  355.                 'token' => $this->getApiKey(),
  356.                 'version' => $version,
  357.                 'cache_version' => $this->getCacheVersion()
  358.             ));
  359.             $response $this->get($endpointUrl$options);
  360.             $this->_save($response$cachekey$version);
  361.         }
  362.         return $this;
  363.     }
  364.     /**
  365.      * Gets a list of datasource entries
  366.      *
  367.      * @param  string $slug Slug
  368.      * @param  array $options Options
  369.      * @return \Storyblok\Client
  370.      */
  371.     public function getDatasourceEntries($slug$options = array())
  372.     {
  373.         $version 'published';
  374.         $endpointUrl 'datasource_entries/';
  375.         if ($this->editModeEnabled) {
  376.             $version 'draft';
  377.         }
  378.         $key 'datasource_entries/' $slug '/' serialize($options);
  379.         $cachekey $this->_getCacheKey($key);
  380.         $this->reCacheOnPublish($key);
  381.         if ($version == 'published' && $this->cache && $cachedItem $this->cache->load($cachekey)) {
  382.             $this->_assignState($cachedItem);
  383.         } else {
  384.             $options array_merge($options, array(
  385.                 'token' => $this->getApiKey(),
  386.                 'version' => $version,
  387.                 'cache_version' => $this->getCacheVersion(),
  388.                 'datasource' => $slug
  389.             ));
  390.             $response $this->get($endpointUrl$options);
  391.             $this->_save($response$cachekey$version);
  392.         }
  393.         return $this;
  394.     }
  395.     /**
  396.      * Gets a list of tags
  397.      *
  398.      * array(
  399.      *    'starts_with' => $slug
  400.      * )
  401.      *
  402.      *
  403.      * @param  array $options Options
  404.      * @return \Storyblok\Client
  405.      */
  406.     public function getLinks($options = array())
  407.     {
  408.         $version 'published';
  409.         $key $this->linksPath;
  410.         $cachekey $this->_getCacheKey($key);
  411.         if ($this->editModeEnabled) {
  412.             $version 'draft';
  413.         }
  414.         if ($version == 'published' && $this->cache && $cachedItem $this->cache->load($cachekey)) {
  415.             $this->_assignState($cachedItem);
  416.         } else {
  417.             $options array_merge($options, array(
  418.                 'token' => $this->getApiKey(),
  419.                 'version' => $version,
  420.                 'cache_version' => $this->getCacheVersion()
  421.             ));
  422.             $response $this->get($key$options);
  423.             $this->_save($response$cachekey$version);
  424.         }
  425.         return $this;
  426.     }
  427.     /**
  428.      * Transforms datasources into a ['name']['value'] array.
  429.      *
  430.      * @return array
  431.      */
  432.     public function getAsNameValueArray()
  433.     {
  434.         if (!isset($this->responseBody)) {
  435.             return array();
  436.         }
  437.         $array = [];
  438.         foreach ($this->responseBody['datasource_entries'] as $entry) {
  439.             if (!isset($array[$entry['name']])) {
  440.                 $array[$entry['name']] = $entry['value'];
  441.             }
  442.         }
  443.         return $array;
  444.     }
  445.     /**
  446.      * Transforms tags into a string array.
  447.      *
  448.      * @return array
  449.      */
  450.     public function getTagsAsStringArray()
  451.     {
  452.         if (!isset($this->responseBody)) {
  453.             return array();
  454.         }
  455.         $array = [];
  456.         foreach ($this->responseBody['tags'] as $entry) {
  457.             array_push($array$entry['name']);
  458.         }
  459.         return $array;
  460.     }
  461.     /**
  462.      * Transforms links into a tree
  463.      *
  464.      * @return array
  465.      */
  466.     public function getAsTree()
  467.     {
  468.         if (!isset($this->responseBody)) {
  469.             return array();
  470.         }
  471.         $tree = [];
  472.         foreach ($this->responseBody['links'] as $item) {
  473.             if (!isset($tree[$item['parent_id']])) {
  474.                 $tree[$item['parent_id']] = array();
  475.             }
  476.             $tree[$item['parent_id']][] = $item;
  477.         }
  478.         return $this->_generateTree(0$tree);
  479.     }
  480.     /**
  481.      * Recursive function to generate tree
  482.      *
  483.      * @param  integer $parent
  484.      * @param  array  $items
  485.      * @return array
  486.      */
  487.     private function _generateTree($parent 0$items)
  488.     {
  489.         $tree = array();
  490.         if (isset($items[$parent])) {
  491.             $result $items[$parent];
  492.             foreach ($result as $item) {
  493.                 if (!isset($tree[$item['id']])) {
  494.                     $tree[$item['id']] = array();
  495.                 }
  496.                 $tree[$item['id']]['item']  = $item;
  497.                 $tree[$item['id']]['children']  = $this->_generateTree($item['id'], $items);
  498.             }
  499.         }
  500.         return $tree;
  501.     }
  502.     /**
  503.      * Save's the current response in the cache if version is published
  504.      *
  505.      * @param  array $response
  506.      * @param  string $key
  507.      * @param  string $version
  508.      */
  509.     private function _save($response$key$version)
  510.     {
  511.         $this->_assignState($response);
  512.         if ($this->cache &&
  513.             $version == 'published' &&
  514.             $response->httpResponseHeaders &&
  515.             $response->httpResponseCode == 200) {
  516.             $this->cache->save($response$key);
  517.         }
  518.     }
  519.     /**
  520.      * Assigns the httpResponseBody and httpResponseHeader to '$this';
  521.      *
  522.      * @param  array $response
  523.      * @param  string $key
  524.      * @param  string $version
  525.      */
  526.     private function _assignState($response) {
  527.         $this->responseBody $response->httpResponseBody;
  528.         $this->responseHeaders $response->httpResponseHeaders;
  529.     }
  530.     /**
  531.      * prepares to Options for the cache key. Fixes some issues for too long filenames if filecache is used.
  532.      *
  533.      * @param  array $response
  534.      * @param  string $key
  535.      * @param  string $version
  536.      */
  537.     private function _prepareOptionsForKey($options) {
  538.         $prepared = array();
  539.         $keyOrder = array();
  540.         foreach($options as $key => $value) {
  541.            array_push($prepared$value);
  542.            array_push($keyOrdersubstr($key01));
  543.        }
  544.        array_push($preparedjoin(''$keyOrder));
  545.        return $prepared;
  546.     }
  547.     private function _getCacheKey($key '')
  548.     {
  549.         return hash('sha256'$key);
  550.     }
  551. }