1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198 |
- <?php
- /*
- * This file is part of the Redisun package.
- *
- * (c) LI Mengxiang <limengxiang876@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Limen\Redisun;
- use Exception;
- use Limen\Redisun\Commands\Command;
- use Limen\Redisun\Commands\Factory;
- use Limen\Redisun\Commands\FactoryInterface;
- use Predis\Client as RedisClient;
- /**
- * CRUD model for redis
- * Class Model
- *
- * @package Limen\Redisun
- *
- * @author LI Mengxiang <limengxiang876@gmail.com>
- */
- abstract class Model
- {
- const TYPE_STRING = 'string';
- const TYPE_SET = 'set';
- const TYPE_SORTED_SET = 'zset';
- const TYPE_LIST = 'list';
- const TYPE_HASH = 'hash';
- // not expired ttl
- const TTL_PERSIST = '-1';
- /**
- * Redis data type
- * @var string
- * Could be string, list, set, zset, hash
- */
- protected $type;
- /**
- * Redis key representation.
- * users:{id}:phone e.g.
- * @var string
- */
- protected $key;
- /**
- * @var string
- */
- protected $delimiter = ':';
- /**
- * Primary key name like database
- * @var string
- */
- protected $primaryFieldName = 'id';
- /**
- * @var string
- */
- protected $fieldWrapper = '{}';
- /**
- * @var QueryBuilder
- */
- protected $queryBuilder;
- /**
- * @var FactoryInterface
- */
- protected $commandFactory;
- /**
- * @var array
- */
- protected $orderBys = [];
- /**
- * offset for pagination
- * @var int
- */
- protected $offset;
- /**
- * limit for pagination
- * @var int
- */
- protected $limit;
- /**
- * Push method for list type
- * @var string
- */
- protected $listPushMethod = 'rpush';
- /**
- * @var RedisClient
- */
- protected $redClient;
- /**
- * if set to true, the subclass must override method compare()
- * @var bool
- */
- protected $sortable = false;
- /**
- * @var array
- */
- private $orderByFieldIndices = [];
- public function __construct($parameters = null, $options = null)
- {
- $this->initRedisClient($parameters, $options);
- $this->newQuery();
- $this->setCommandFactory();
- }
- /**
- * Refresh query builder
- * @return $this
- */
- public function newQuery()
- {
- $this->orderBys = [];
- $this->limit = null;
- $this->offset = null;
- return $this->freshQueryBuilder();
- }
- /**
- * @param $factory FactoryInterface
- */
- public function setCommandFactory($factory = null)
- {
- $this->commandFactory = $factory ?: new Factory();
- }
- /**
- * @return FactoryInterface
- */
- public function getCommandFactory()
- {
- return $this->commandFactory;
- }
- /**
- * @return QueryBuilder
- */
- public function getQueryBuilder()
- {
- return $this->queryBuilder;
- }
- /**
- * @return string
- */
- public function getPrimaryFieldName()
- {
- return $this->primaryFieldName;
- }
- /**
- * Query like database
- * The {$bindingKey} part in the key representation would be replace by $value
- * @param $field string
- * @param $value string
- * @return $this
- */
- public function where($field, $value)
- {
- $this->queryBuilder->whereEqual($field, $value);
- return $this;
- }
- /**
- * @param $field
- * @param array $values
- * @return $this
- */
- public function whereIn($field, array $values)
- {
- $this->queryBuilder->whereIn($field, $values);
- return $this;
- }
- /**
- * @param $field
- * @param string $order
- * @return $this
- */
- public function orderBy($field, $order = 'asc')
- {
- $this->orderBys[$field] = $order;
- return $this;
- }
- /**
- * @param int $offset
- * @return $this
- */
- public function skip($offset)
- {
- $this->offset = $offset;
- return $this;
- }
- /**
- * @param int $limit
- * @return $this
- */
- public function take($limit)
- {
- $this->limit = $limit;
- return $this;
- }
- /**
- * Get all items
- * @return array
- */
- public function all()
- {
- $this->newQuery();
- return $this->get();
- }
- /**
- * Retrieve items
- * @return array
- */
- public function get()
- {
- $data = $this->getProxy();
- return $data;
- }
- /**
- * @return int
- */
- public function count()
- {
- return count($this->prepareKeys());
- }
- /**
- * @param string $order asc|desc
- * @return array
- * @throws Exception
- */
- public function sort($order = 'asc')
- {
- $this->checkSortable();
- $values = $this->get();
- if (!$values) {
- return [];
- }
- if ($order == 'asc') {
- usort($values, [$this, 'compare']);
- } else {
- usort($values, [$this, 'revcompare']);
- }
- return $values;
- }
- /**
- * @return mixed
- * @throws Exception
- */
- public function max()
- {
- $this->checkSortable();
- $values = $this->get();
- if (!$values) {
- return null;
- }
- $max = array_pop($values);
- foreach ($values as $v) {
- if ($this->compare($v, $max) === 1) {
- $max = $v;
- }
- }
- return $max;
- }
- /**
- * @return mixed
- * @throws Exception
- */
- public function min()
- {
- $this->checkSortable();
- $values = $this->get();
- if (!$values) {
- return null;
- }
- $min = array_pop($values);
- foreach ($values as $v) {
- if ($this->compare($v, $min) === -1) {
- $min = $v;
- }
- }
- return $min;
- }
- /**
- * @return array
- */
- public function getKeys()
- {
- return $this->prepareKeys();
- }
- /**
- * @return array
- */
- public function getCompleteKeys()
- {
- return $this->prepareCompleteKeys();
- }
- /**
- * @return number
- */
- public function sum()
- {
- $values = $this->get();
- return array_sum($values);
- }
- /**
- * Retrieve first item
- * @return mixed|null
- */
- public function first()
- {
- $items = $this->take(1)->get();
- return $items ? array_shift($items) : null;
- }
- /**
- * Create an item
- * @param $id int|string Primary key
- * @param $value mixed
- * @param int $ttl
- * @param bool|null check key exists or not before create, not check if null
- * @return bool
- */
- public function create($id, $value, $ttl = null, $exists = null)
- {
- $this->newQuery();
- $queryKey = $this->queryBuilder->whereEqual($this->primaryFieldName, $id)->firstQueryKey();
- if (!$this->isCompleteKey($queryKey)) {
- return false;
- }
- return $this->insertProxy($queryKey, $value, $ttl, $exists);
- }
- /**
- * Similar to setnx
- * @param $id
- * @param $value
- * @param null $ttl
- * @return bool
- */
- public function createNotExists($id, $value, $ttl = null)
- {
- return $this->create($id, $value, $ttl, false);
- }
- /**
- * Similar to setxx
- * @param $id
- * @param $value
- * @param null $ttl
- * @return bool
- */
- public function createExists($id, $value, $ttl = null)
- {
- return $this->create($id, $value, $ttl, true);
- }
- /**
- * @param array $bindings
- * @param $value
- * @param int $ttl
- * @param bool $exists
- * @return mixed
- */
- public function insert(array $bindings, $value, $ttl = null, $exists = null)
- {
- $this->newQuery();
- foreach ($bindings as $k => $v) {
- $this->queryBuilder->whereEqual($k, $v);
- }
- $queryKey = $this->queryBuilder->firstQueryKey();
- if (!$this->isCompleteKey($queryKey)) {
- return false;
- }
- return $this->insertProxy($queryKey, $value, $ttl, $exists);
- }
- /**
- * Insert when key exists
- *
- * @param array $bindings
- * @param $value
- * @param null $ttl
- * @return mixed
- */
- public function insertExists(array $bindings, $value, $ttl = null)
- {
- return $this->insert($bindings, $value, $ttl, true);
- }
- /**
- * Insert when key not exists
- *
- * @param array $bindings
- * @param $value
- * @param null $ttl
- * @return mixed
- */
- public function insertNotExists(array $bindings, $value, $ttl = null)
- {
- return $this->insert($bindings, $value, $ttl, false);
- }
- /**
- * find an item
- * @param $id int|string Primary key
- * @return mixed
- */
- public function find($id)
- {
- $this->newQuery();
- $this->queryBuilder->whereEqual($this->primaryFieldName, $id);
- $queryKey = $this->queryBuilder->firstQueryKey();
- if (!$this->isCompleteKey($queryKey)) {
- return null;
- }
- list($method, $parameters) = $this->getFindMethodAndParameters();
- array_unshift($parameters, $queryKey);
- $value = call_user_func_array([$this->redClient, $method], $parameters);
- return $value;
- }
- /**
- * Update items, need to use where() first
- * @param $value
- * @param int $ttl ttl in second
- * @return mixed
- */
- public function update($value, $ttl = null)
- {
- $queryKeys = $this->prepareKeys(false);
- if (count($queryKeys)) {
- return $this->updateBatchProxy($queryKeys, $value, $ttl);
- }
- return false;
- }
- /**
- * Delete items
- * @return bool|int
- */
- public function delete()
- {
- $queryKeys = $this->prepareKeys(false);
- if (count($queryKeys) > 0) {
- return $this->redClient->del($queryKeys);
- }
- return false;
- }
- /**
- * Destroy item
- * @param string $id primary key
- * @return bool
- * @throws Exception
- */
- public function destroy($id)
- {
- $this->newQuery();
- $queryKey = $this->queryBuilder->whereEqual($this->primaryFieldName, $id)->firstQueryKey();
- if (!$this->isCompleteKey($queryKey)) {
- return false;
- }
- return (bool)$this->redClient->del([$queryKey]);
- }
- /**
- * @param array $ids primary keys
- * @return array
- * @throws Exception
- */
- public function findBatch(array $ids)
- {
- $primaryKeys = [];
- foreach ($ids as $id) {
- $primaryKeys[$id] = $this->getPrimaryKey($id);
- }
- $this->newQuery()->whereIn($this->getPrimaryFieldName(), $ids);
- $queryKeys = $this->prepareCompleteKeys();
- if (!$queryKeys) {
- return [];
- }
- $data = $this->getProxy($queryKeys);
- $list = [];
- foreach ($data as $k => $v) {
- $id = array_search($k, $primaryKeys);
- if ($id) {
- $list[$id] = $v;
- }
- }
- return $list;
- }
- /**
- * @param array $ids primary keys
- * @return int
- */
- public function destroyBatch(array $ids)
- {
- $this->newQuery()->whereIn($this->getPrimaryFieldName(), $ids);
- $queryKeys = $this->prepareCompleteKeys();
- if (!$queryKeys) {
- return false;
- }
- return $this->redClient->del($queryKeys);
- }
- /**
- * @param array $ids primary keys
- * @param $value
- * @param int|null $ttl
- * @return mixed
- */
- public function updateBatch(array $ids, $value, $ttl = null)
- {
- $this->newQuery()->whereIn($this->getPrimaryFieldName(), $ids);
- $queryKeys = $this->prepareCompleteKeys();
- if (!$queryKeys) {
- return false;
- }
- return $this->updateBatchProxy($queryKeys, $value, $ttl);
- }
- /**
- * Get key and set new value
- *
- * @param string|array $value
- * @param null $ttl
- * @return mixed
- * @throws Exception
- */
- public function getAndSet($value, $ttl = null)
- {
- $keys = $this->queryBuilder->getQueryKeys();
- if (count($keys) > 1) {
- throw new Exception('GetAndSet doesnt support multiple keys');
- } elseif (count($keys) == 0) {
- throw new Exception('No query keys');
- }
- $key = $keys[0];
- if (!$this->isCompleteKey($key)) {
- throw new Exception('Not complete key');
- }
- $value = $this->castValueForUpdate($value);
- $commandName = 'getset' . ucfirst($this->type);
- $command = $this->commandFactory->getCommand($commandName, [$key], $value);
- if (!is_null($ttl)) {
- $command->setTtl($ttl);
- }
- $result = $this->executeCommand($command);
- $data = isset($result[$key]) ? $result[$key] : null;
- if ($data && $this->type == static::TYPE_HASH) {
- $data = $this->resolveHash($data);
- }
- return $data;
- }
- /**
- * Call Predis function
- * @param $method
- * @param array $parameters
- * @return mixed
- * @throws \Exception
- */
- public function __call($method, $parameters = [])
- {
- $keys = $this->queryBuilder->getQueryKeys();
- if (count($keys) > 1) {
- throw new Exception('More than one key had been built and redis built-in method "' . $method . '" dont support batch operation.');
- } elseif (count($keys) === 0) {
- throw new Exception('No query keys had been built, need to use where() first.');
- }
- array_unshift($parameters, $keys[0]);
- return call_user_func_array([$this->redClient, $method], $parameters);
- }
- /**
- * Compare items to sort
- * @param $a
- * @param $b
- * @return int 1.a>b 0.a=b -1.a<b
- */
- protected function compare($a, $b)
- {
- return $a > $b ? 1 : ($a == $b ? 0 : -1);
- }
- /**
- * @param $a
- * @param $b
- * @return int
- */
- protected function revcompare($a, $b)
- {
- return -$this->compare($a, $b);
- }
- /**
- * Initialize redis client
- *
- * @param $parameters
- * @param $options
- */
- protected function initRedisClient($parameters, $options)
- {
- $this->redClient = new RedisClient($parameters, $options);
- }
- /**
- * Prepare query keys
- * @param bool $forGet
- * @return array
- */
- protected function prepareKeys($forGet = true)
- {
- $queryKeys = $this->queryBuilder->getQueryKeys();
- // Caution! Would get all items.
- if (!$queryKeys) {
- $queryKeys = [$this->key];
- }
- $existKeys = $this->getExistKeys($queryKeys);
- if ($forGet) {
- $this->setOrderByFieldIndices();
- if ($this->orderByFieldIndices) {
- uasort($existKeys, [$this, 'sortByFields']);
- }
- if ($this->offset || $this->limit) {
- $existKeys = array_slice($existKeys, (int)$this->offset, $this->limit);
- }
- }
- return $existKeys;
- }
- /**
- * @return array
- */
- protected function prepareCompleteKeys()
- {
- $keys = $this->queryBuilder->getQueryKeys();
- if (!$keys) {
- return [];
- }
- return array_filter($keys, [$this, 'isCompleteKey']);
- }
- /**
- * @param $key
- * @return bool
- */
- protected function isCompleteKey($key)
- {
- return !$this->hasUnboundField($key);
- }
- /**
- * @param $key
- * @param $value
- * @param null $ttl
- * @param null|bool $exists
- * @return bool
- */
- protected function insertProxy($key, $value, $ttl = null, $exists = null)
- {
- $method = $this->getUpdateMethod();
- if (!$method) {
- return false;
- }
- $value = $this->castValueForUpdate($value);
- $command = $this->commandFactory->getCommand($method, [$key], $value);
- if ($ttl) {
- $command->setTtl($ttl);
- }
- if ($exists === false) {
- $command->pleaseNotExists();
- } elseif ($exists === true) {
- $command->pleaseExists();
- }
- $command->pleaseDeleteIfExists();
- $response = $this->executeCommand($command);
- return isset($response[$key]) && $response[$key];
- }
- /**
- * @param $keys
- * @param $value
- * @param int $ttl ttl in second
- * @return bool
- */
- protected function updateBatchProxy($keys, $value, $ttl = null)
- {
- $method = $this->getUpdateMethod();
- if (empty($method)) {
- return false;
- }
- $value = $this->castValueForUpdate($value);
- $command = $this->commandFactory->getCommand($method, $keys, $value);
- $command->pleaseDeleteIfExists();
- if ($ttl) {
- $command->setTtl($ttl);
- }
- return $this->executeCommand($command);
- }
- /**
- * @param $queryKeys
- * @return array
- */
- protected function getProxy($queryKeys = null)
- {
- if ($queryKeys === null) {
- $queryKeys = $this->prepareKeys();
- }
- $data = [];
- if ($queryKeys) {
- list($method, $params) = $this->getFindMethodAndParameters();
- $command = $this->commandFactory->getCommand($method, $queryKeys);
- $data = $this->executeCommand($command);
- }
- if ($data && $this->type == static::TYPE_HASH) {
- $data = $this->resolveHashes($data);
- }
- return $data;
- }
- /**
- * @return $this
- */
- protected function freshQueryBuilder()
- {
- $this->queryBuilder = new QueryBuilder($this->key);
- $keyParts = $this->explodeKey($this->key);
- foreach ($keyParts as $part) {
- if ($this->isUnboundField($part)) {
- $this->queryBuilder->setFieldNeedle($this->trimWrapper($part), $part);
- }
- }
- return $this;
- }
- /**
- * Get update method according to redis data type
- *
- * @return string
- */
- protected function getUpdateMethod()
- {
- $method = '';
- switch ($this->type) {
- case 'string':
- $method = 'set';
- break;
- case 'list':
- $method = $this->listPushMethod;
- break;
- case 'set':
- $method = 'sadd';
- break;
- case 'zset':
- $method = 'zadd';
- break;
- case 'hash':
- $method = 'hmset';
- break;
- default:
- break;
- }
- return $method;
- }
- /**
- * Cast value data type for update according to redis data type
- *
- * @param $value
- * @return array
- */
- protected function castValueForUpdate($value)
- {
- switch ($this->type) {
- case 'string':
- $value = [(string)$value];
- break;
- case 'list':
- case 'set':
- $value = (array)$value;
- break;
- case 'zset':
- $casted = [];
- foreach ($value as $k => $v) {
- $casted[] = $v;
- $casted[] = $k;
- }
- $value = $casted;
- break;
- case 'hash':
- $casted = [];
- foreach ($value as $k => $v) {
- $casted[] = $k;
- $casted[] = $v;
- }
- $value = $casted;
- break;
- default:
- break;
- }
- return $value;
- }
- /**
- * Get find method and default parameters according to redis data type.
- * @return array
- */
- protected function getFindMethodAndParameters()
- {
- $method = '';
- $parameters = [];
- switch ($this->type) {
- case 'string':
- $method = 'get';
- break;
- case 'list':
- $method = 'lrange';
- $parameters = [0, -1];
- break;
- case 'set':
- $method = 'smembers';
- break;
- case 'zset':
- $method = 'zrange';
- $parameters = [0, -1];
- break;
- case 'hash':
- $method = 'hgetall';
- break;
- default:
- break;
- }
- return [$method, $parameters];
- }
- /**
- * Get existed keys in redis database
- *
- * @param $queryKeys
- * @return array|mixed
- */
- protected function getExistKeys($queryKeys)
- {
- $keys = $this->markUnboundFields($queryKeys);
- $exist = [];
- if ($keys) {
- $command = $this->commandFactory->getCommand('keys', $keys);
- $exist = $this->executeCommand($command);
- $exist = array_unique($exist);
- }
- return $exist;
- }
- /**
- * @param Command $command
- * @return mixed
- */
- protected function executeCommand($command)
- {
- $evalArgs = $command->getArguments();
- array_unshift($evalArgs, $command->getKeysCount());
- try {
- array_unshift($evalArgs, sha1($command->getScript()));
- $data = call_user_func_array([$this->redClient, 'evalsha'], $evalArgs);
- } catch (\Exception $e) {
- if (strpos($e->getMessage(), 'NOSCRIPT') !== false) {
- $evalArgs[0] = $command->getScript();
- $data = call_user_func_array([$this->redClient, 'eval'], $evalArgs);
- } else {
- throw $e;
- }
- }
- $data = $command->parseResponse($data);
- return $data;
- }
- /**
- * Check a key whether has unbound field
- *
- * @param $key
- * @return bool
- */
- protected function hasUnboundField($key)
- {
- $parts = $this->explodeKey($key);
- foreach ($parts as $part) {
- if ($this->isUnboundField($part)) {
- return true;
- }
- }
- return false;
- }
- /**
- * @param string $part key particle
- * @return bool|string
- */
- protected function getFieldName($part)
- {
- if ($this->isUnboundField($part)) {
- return substr($part, 1, -1);
- }
- return false;
- }
- /**
- * Mark unbound field with *
- *
- * @param $keys
- * @return array
- */
- protected function markUnboundFields($keys)
- {
- $marked = [];
- foreach ($keys as $key) {
- $parts = $this->explodeKey($key);
- foreach ($parts as &$part) {
- if ($this->isUnboundField($part)) {
- $part = '*';
- }
- }
- $marked[] = $this->joinToKey($parts);
- }
- return $marked;
- }
- /**
- * Compare two keys by key field(s)
- *
- * @param $key1
- * @param $key2
- * @return int
- */
- protected function sortByFields($key1, $key2)
- {
- $key1Parts = $this->explodeKey($key1);
- $key2Parts = $this->explodeKey($key2);
- $flag = 0;
- foreach ($this->orderByFieldIndices as $index => $order) {
- if ($flag !== 0) {
- break;
- }
- if ($key1Parts[$index] > $key2Parts[$index]) {
- $flag = $order == 'asc' ? 1 : -1;
- } elseif ($key1Parts[$index] < $key2Parts[$index]) {
- $flag = $order == 'asc' ? -1 : 1;
- } else {
- $flag = 0;
- }
- }
- return $flag;
- }
- /**
- * @param string $field
- * @return string
- */
- protected function getFieldNeedle($field)
- {
- return $this->fieldWrapper[0] . $field . $this->fieldWrapper[1];
- }
- /**
- * @param $id
- * @return mixed
- */
- protected function getPrimaryKey($id)
- {
- return str_replace($this->getFieldNeedle($this->getPrimaryFieldName()), $id, $this->key);
- }
- /**
- * @throws Exception
- */
- private function checkSortable()
- {
- if (!$this->sortable) {
- throw new Exception(get_class($this) . ' is not sortable.');
- }
- }
- /**
- * Set order by field and order
- */
- private function setOrderByFieldIndices()
- {
- $keyParts = $this->explodeKey($this->key);
- foreach ($this->orderBys as $field => $order) {
- $needle = $this->fieldWrapper[0] . $field . $this->fieldWrapper[1];
- $this->orderByFieldIndices[array_search($needle, $keyParts)] = $order;
- }
- }
- /**
- * @param $key
- * @return array
- */
- private function explodeKey($key)
- {
- return explode($this->delimiter, $key);
- }
- /**
- * @param $parts
- * @return string
- */
- private function joinToKey($parts)
- {
- return join($this->delimiter, $parts);
- }
- /**
- * @param $part
- * @return bool
- */
- private function isUnboundField($part)
- {
- return $this->fieldWrapper[0] === $part[0]
- && $this->fieldWrapper[1] === $part[strlen($part) - 1];
- }
- /**
- * @param $part
- * @return bool|string
- */
- private function trimWrapper($part)
- {
- return substr($part, 1, -1);
- }
- /**
- * raw hash data to associate array
- * @param array $hashes
- * @return array
- */
- private function resolveHashes(array $hashes)
- {
- $assoc = [];
- foreach ($hashes as $k => $hash) {
- $item = $this->resolveHash($hash);
- if ($item) {
- $assoc[$k] = $item;
- }
- }
- return $assoc;
- }
- /**
- * @param $hash
- * @return array
- */
- private function resolveHash($hash)
- {
- $array = [];
- for ($i = 0; $i < count($hash); $i = $i + 2) {
- $array[$hash[$i]] = $hash[$i + 1];
- }
- return $array;
- }
- }
|