├── .gitattributes ├── .gitignore ├── .phpstorm.meta.php ├── src ├── Exceptions │ ├── Exception.php │ ├── HttpException.php │ └── InvalidArgumentException.php ├── ConfigProvider.php ├── IM.php ├── Traits │ └── HasHttpRequest.php └── Support │ └── Config.php ├── publish └── im.php ├── phpunit.xml ├── .travis.yml ├── LICENSE ├── composer.json ├── .php_cs └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | *.cache 4 | *.log 5 | .idea/ -------------------------------------------------------------------------------- /.phpstorm.meta.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Hedeqiang\IM\Exceptions; 13 | 14 | class Exception extends \Exception 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Exceptions/HttpException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Hedeqiang\IM\Exceptions; 13 | 14 | class HttpException extends Exception 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Hedeqiang\IM\Exceptions; 13 | 14 | class InvalidArgumentException extends Exception 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /publish/im.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | return [ 13 | 'sdk_app_id' => env('SDK_APP_ID', ''), 14 | 'identifier' => env('IDENTIFIER', ''), 15 | 'secret_key' => env('SECRET_KEY', ''), 16 | ]; 17 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | ./tests/ 14 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | - php: 7.2 8 | env: SW_VERSION="4.4.7" 9 | - php: 7.3 10 | env: SW_VERSION="4.4.7" 11 | - php: master 12 | env: SW_VERSION="4.4.7" 13 | 14 | allow_failures: 15 | - php: master 16 | 17 | services: 18 | - mysql 19 | - redis-server 20 | - docker 21 | 22 | before_install: 23 | - export PHP_MAJOR="$(`phpenv which php` -r 'echo phpversion();' | cut -d '.' -f 1)" 24 | - export PHP_MINOR="$(`phpenv which php` -r 'echo phpversion();' | cut -d '.' -f 2)" 25 | - echo $PHP_MAJOR 26 | - echo $PHP_MINOR 27 | 28 | install: 29 | - cd $TRAVIS_BUILD_DIR 30 | - bash ./tests/swoole.install.sh 31 | - phpenv config-rm xdebug.ini || echo "xdebug not available" 32 | - phpenv config-add ./tests/ci.ini 33 | 34 | before_script: 35 | - cd $TRAVIS_BUILD_DIR 36 | - composer config -g process-timeout 900 && composer update 37 | 38 | script: 39 | - composer analyze 40 | - composer test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 hedeqiang/im 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ConfigProvider.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Hedeqiang\IM; 15 | 16 | class ConfigProvider 17 | { 18 | public function __invoke(): array 19 | { 20 | return [ 21 | 'dependencies' => [ 22 | ], 23 | 'commands' => [ 24 | ], 25 | 'annotations' => [ 26 | 'scan' => [ 27 | 'paths' => [ 28 | __DIR__, 29 | ], 30 | ], 31 | ], 32 | 'publish' => [ 33 | [ 34 | 'id' => 'config', 35 | 'description' => 'The config of im.', 36 | 'source' => __DIR__.'/../publish/im.php', 37 | 'destination' => BASE_PATH.'/config/autoload/im.php', 38 | ], 39 | ], 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hedeqiang/im", 3 | "type": "library", 4 | "license": "MIT", 5 | "keywords": [ 6 | "php", 7 | "hyperf", 8 | "IM", 9 | "腾讯云", 10 | "即时通信" 11 | ], 12 | "description": "腾讯云即时通信 SDK for Hyperf", 13 | "autoload": { 14 | "psr-4": { 15 | "Hedeqiang\\IM\\": "./src/" 16 | } 17 | }, 18 | "autoload-dev": { 19 | "psr-4": { 20 | "HyperfTest\\": "tests" 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.2", 25 | "ext-swoole": ">=4.5", 26 | "tencent/tls-sig-api-v2": "^1.0" 27 | }, 28 | "require-dev": { 29 | "friendsofphp/php-cs-fixer": "^2.14", 30 | "phpstan/phpstan": "^0.12", 31 | "hyperf/testing": "2.0.*", 32 | "swoft/swoole-ide-helper": "dev-master" 33 | }, 34 | "config": { 35 | "sort-packages": true 36 | }, 37 | "scripts": { 38 | "test": "co-phpunit -c phpunit.xml --colors=always", 39 | "analyze": "phpstan analyse --memory-limit 1024M -l 0 ./src", 40 | "cs-fix": "php-cs-fixer fix $1" 41 | }, 42 | "extra": { 43 | "hyperf": { 44 | "config": "Hedeqiang\\IM\\ConfigProvider" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 14 | ->setRules([ 15 | '@PSR2' => true, 16 | '@Symfony' => true, 17 | '@DoctrineAnnotation' => true, 18 | '@PhpCsFixer' => true, 19 | 'header_comment' => [ 20 | 'commentType' => 'PHPDoc', 21 | 'header' => $header, 22 | 'separate' => 'none', 23 | 'location' => 'after_declare_strict', 24 | ], 25 | 'array_syntax' => [ 26 | 'syntax' => 'short' 27 | ], 28 | 'list_syntax' => [ 29 | 'syntax' => 'short' 30 | ], 31 | 'concat_space' => [ 32 | 'spacing' => 'one' 33 | ], 34 | 'blank_line_before_statement' => [ 35 | 'statements' => [ 36 | 'declare', 37 | ], 38 | ], 39 | 'general_phpdoc_annotation_remove' => [ 40 | 'annotations' => [ 41 | 'author' 42 | ], 43 | ], 44 | 'ordered_imports' => [ 45 | 'imports_order' => [ 46 | 'class', 'function', 'const', 47 | ], 48 | 'sort_algorithm' => 'alpha', 49 | ], 50 | 'single_line_comment_style' => [ 51 | 'comment_types' => [ 52 | ], 53 | ], 54 | 'yoda_style' => [ 55 | 'always_move_variable' => false, 56 | 'equal' => false, 57 | 'identical' => false, 58 | ], 59 | 'phpdoc_align' => [ 60 | 'align' => 'left', 61 | ], 62 | 'multiline_whitespace_before_semicolons' => [ 63 | 'strategy' => 'no_multi_line', 64 | ], 65 | 'class_attributes_separation' => true, 66 | 'combine_consecutive_unsets' => true, 67 | 'declare_strict_types' => true, 68 | 'linebreak_after_opening_tag' => true, 69 | 'lowercase_constants' => true, 70 | 'lowercase_static_reference' => true, 71 | 'no_useless_else' => true, 72 | 'no_unused_imports' => true, 73 | 'not_operator_with_successor_space' => true, 74 | 'not_operator_with_space' => false, 75 | 'ordered_class_elements' => true, 76 | 'php_unit_strict' => false, 77 | 'phpdoc_separation' => false, 78 | 'single_quote' => true, 79 | 'standardize_not_equals' => true, 80 | 'multiline_comment_opening_closing' => true, 81 | ]) 82 | ->setFinder( 83 | PhpCsFixer\Finder::create() 84 | ->exclude('vendor') 85 | ->in(__DIR__) 86 | ) 87 | ->setUsingCache(false); 88 | -------------------------------------------------------------------------------- /src/IM.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Hedeqiang\IM; 13 | 14 | use Hedeqiang\IM\Exceptions\Exception; 15 | use Hedeqiang\IM\Exceptions\HttpException; 16 | use Hedeqiang\IM\Support\Config; 17 | use Hedeqiang\IM\Traits\HasHttpRequest; 18 | use Psr\SimpleCache\CacheInterface; 19 | use Tencent\TLSSigAPIv2; 20 | use Hyperf\Utils\ApplicationContext; 21 | 22 | class IM 23 | { 24 | use HasHttpRequest; 25 | 26 | const ENDPOINT_TEMPLATE = 'https://console.tim.qq.com/%s/%s/%s?%s'; 27 | 28 | const ENDPOINT_VERSION = 'v4'; 29 | 30 | const ENDPOINT_FORMAT = 'json'; 31 | 32 | /** 33 | * @var Config 34 | */ 35 | protected $config; 36 | 37 | public function __construct(array $config) 38 | { 39 | $this->config = new Config($config); 40 | } 41 | 42 | /** 43 | * @param string $servername 44 | * @param string $command 45 | * 46 | * @return array 47 | * 48 | * @throws Exception 49 | * @throws HttpException 50 | */ 51 | public function send($servername, $command, array $params = []) 52 | { 53 | try { 54 | $result = $this->postJson($this->buildEndpoint($servername, $command), $params); 55 | } catch (\Exception $e) { 56 | throw new HttpException($e->getMessage(), $e->getCode(), $e); 57 | } 58 | 59 | if (0 === $result['ErrorCode'] && 'OK' === $result['ActionStatus']) { 60 | return $result; 61 | } 62 | 63 | throw new Exception('Tim REST API error: '.json_encode($result)); 64 | } 65 | 66 | /** 67 | * Build endpoint url. 68 | * 69 | * @throws \Exception 70 | */ 71 | protected function buildEndpoint(string $servername, string $command): string 72 | { 73 | $query = http_build_query([ 74 | 'sdkappid' => $this->config->get('sdk_app_id'), 75 | 'identifier' => $this->config->get('identifier'), 76 | 'usersig' => $this->generateSign($this->config->get('identifier')), 77 | 'random' => mt_rand(0, 4294967295), 78 | 'contenttype' => self::ENDPOINT_FORMAT, 79 | ]); 80 | 81 | return \sprintf(self::ENDPOINT_TEMPLATE, self::ENDPOINT_VERSION, $servername, $command, $query); 82 | } 83 | 84 | /** 85 | * Generate Sign. 86 | * 87 | * @throws \Exception 88 | */ 89 | protected function generateSign(string $identifier, int $expires = 15552000): string 90 | { 91 | $cache = $this->di()->get(CacheInterface::class); 92 | 93 | if (!$cache->has($identifier.'_cache')) { 94 | $api = new TLSSigAPIv2($this->config->get('sdk_app_id'), $this->config->get('secret_key')); 95 | $sign = $api->genUserSig($identifier, $expires); 96 | $cache->set($identifier.'_cache', $sign, $expires); 97 | 98 | return $sign; 99 | } 100 | 101 | return $cache->get($identifier.'_cache'); 102 | } 103 | 104 | /** 105 | * Finds an entry of the container by its identifier and returns it. 106 | * 107 | * @param mixed|null $id 108 | * 109 | * @return mixed|\Psr\Container\ContainerInterface 110 | */ 111 | protected function di($id = null) 112 | { 113 | $container = ApplicationContext::getContainer(); 114 | if ($id) { 115 | return $container->get($id); 116 | } 117 | 118 | return $container; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Traits/HasHttpRequest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Hedeqiang\IM\Traits; 13 | 14 | use GuzzleHttp\Client; 15 | use Psr\Http\Message\ResponseInterface; 16 | 17 | /** 18 | * Trait HasHttpRequest. 19 | */ 20 | trait HasHttpRequest 21 | { 22 | /** 23 | * @var HandlerStack 24 | */ 25 | protected $stack; 26 | 27 | /** 28 | * Make a get request. 29 | * 30 | * @param string $endpoint 31 | * @param array $query 32 | * @param array $headers 33 | * 34 | * @return array 35 | */ 36 | protected function get($endpoint, $query = [], $headers = []) 37 | { 38 | return $this->request('get', $endpoint, [ 39 | 'headers' => $headers, 40 | 'query' => $query, 41 | ]); 42 | } 43 | 44 | /** 45 | * Make a post request. 46 | * 47 | * @param string $endpoint 48 | * @param array $params 49 | * @param array $headers 50 | * 51 | * @return array 52 | */ 53 | protected function post($endpoint, $params = [], $headers = []) 54 | { 55 | return $this->request('post', $endpoint, [ 56 | 'headers' => $headers, 57 | 'form_params' => $params, 58 | ]); 59 | } 60 | 61 | /** 62 | * Make a post request with json params. 63 | * 64 | * @param $endpoint 65 | * @param array $params 66 | * @param array $headers 67 | * 68 | * @return array 69 | */ 70 | protected function postJson($endpoint, $params = [], $headers = []) 71 | { 72 | return $this->request('post', $endpoint, [ 73 | 'headers' => $headers, 74 | 'json' => $params, 75 | ]); 76 | } 77 | 78 | /** 79 | * Make a http request. 80 | * 81 | * @param string $method 82 | * @param string $endpoint 83 | * @param array $options http://docs.guzzlephp.org/en/latest/request-options.html 84 | * 85 | * @return array 86 | */ 87 | protected function request($method, $endpoint, $options = []) 88 | { 89 | return $this->unwrapResponse($this->getHttpClient($this->getBaseOptions())->{$method}($endpoint, $options)); 90 | } 91 | 92 | /** 93 | * Return base Guzzle options. 94 | * 95 | * @return array 96 | */ 97 | protected function getBaseOptions() 98 | { 99 | $options = [ 100 | 'base_uri' => method_exists($this, 'getBaseUri') ? $this->getBaseUri() : '', 101 | 'timeout' => method_exists($this, 'getTimeout') ? $this->getTimeout() : 10.0, 102 | ]; 103 | 104 | return $options; 105 | } 106 | 107 | /** 108 | * Return http client. 109 | * 110 | * @return \GuzzleHttp\Client 111 | * 112 | * @codeCoverageIgnore 113 | */ 114 | protected function getHttpClient(array $options = []) 115 | { 116 | return make(Client::class, $options); 117 | } 118 | 119 | protected function getHandlerStack(): HandlerStack 120 | { 121 | if ($this->stack instanceof HandlerStack) { 122 | return $this->stack; 123 | } 124 | 125 | return $this->stack = di()->get(HandlerStackFactory::class)->create(); 126 | } 127 | 128 | /** 129 | * Convert response contents to json. 130 | * 131 | * @return ResponseInterface|array|string 132 | */ 133 | protected function unwrapResponse(ResponseInterface $response) 134 | { 135 | $contentType = $response->getHeaderLine('Content-Type'); 136 | $contents = $response->getBody()->getContents(); 137 | if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) { 138 | return json_decode($contents, true); 139 | } elseif (false !== stripos($contentType, 'xml')) { 140 | return json_decode(json_encode(simplexml_load_string($contents)), true); 141 | } 142 | 143 | return $contents; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

腾讯云 IM 服务端 SDK for Hyperf

2 | 3 |

腾讯云 IM.

4 | 5 | > 普通 PHP 请使用 [hedeqiang/ten-im](https://github.com/hedeqiang/IM) 扩展 6 | ## 前提 7 | 使用本扩展前需要登录 [即时通信 IM 控制台](https://console.cloud.tencent.com/avc) 创建应用,配置管理员、获取 app_id、Key 等关键信息 8 | 9 | 更多请查看并熟读 [即时通信 IM 服务端API](https://cloud.tencent.com/document/product/269/32688) 10 | 11 | [REST API 接口列表](https://cloud.tencent.com/document/product/269/1520) 12 | ## 安装 13 | 14 | ```shell 15 | $ composer require hedeqiang/im -vvv 16 | ``` 17 | 18 | ## 发布配置 19 | ```php 20 | php bin/hyperf.php vendor:publish hedeqiang/im 21 | ``` 22 | 23 | 编辑 `.env` 文件 24 | ```php 25 | SDK_APP_ID= 26 | IDENTIFIER= 27 | SECRET_KEY= 28 | ``` 29 | 30 | ## 使用 31 | ### 获取用户在线状态 32 | ```php 33 | config; 54 | $im = new IM($config); 55 | $params = [ 56 | 'To_Account' => ['hedeqiang'] 57 | ]; 58 | 59 | print_r($im->send('openim','querystate',$params)); 60 | } 61 | } 62 | 63 | ``` 64 | 返回示例 65 | ```php 66 | { 67 | "ActionStatus": "OK", 68 | "ErrorInfo": "", 69 | "ErrorCode": 0, 70 | "QueryResult": [{ 71 | "To_Account": "hedeqiang", 72 | "State": "Offline" 73 | }] 74 | } 75 | ``` 76 | ### 设置资料 77 | ```php 78 | $params = [ 79 | 'From_Account' => 'hedeqiang', 80 | 'ProfileItem' => [ 81 | ['Tag' => 'Tag_Profile_IM_Nick', 'Value' => 'hedeqiang'], 82 | ['Tag' => 'Tag_Profile_IM_Gender', 'Value' => 'Gender_Type_Male'], 83 | ['Tag' => 'Tag_Profile_IM_BirthDay', 'Value' => 19940410], 84 | ['Tag' => 'Tag_Profile_IM_SelfSignature', 'Value' => '程序人生的寂静欢喜'], 85 | ['Tag' => 'Tag_Profile_IM_Image', 'Value' => 'https://upyun.laravelcode.cn/upload/avatar/1524205770e4fbfbff-86ae-3bf9-b7b8-e0e70ce14553.png'], 86 | ], 87 | ]; 88 | 89 | print_r($im->send('profile','portrait_set',$params)); 90 | ``` 91 | 92 | 返回示例: 93 | ```php 94 | { 95 | "ActionStatus": "OK", 96 | "ErrorCode": 0, 97 | "ErrorInfo": "", 98 | "ErrorDisplay": "" 99 | } 100 | ``` 101 | 102 | ### 单发单聊消息 103 | ```php 104 | $params = [ 105 | 'SyncOtherMachine' => 1, // 消息不同步至发送方 106 | 'From_Account' => 'hedeqiang', 107 | 'To_Account' => 'zhangsan', 108 | 'MsgRandom' => 1287657, 109 | 'MsgTimeStamp' => 1557387418, 110 | 'MsgBody' => [ 111 | [ 112 | 'MsgType' => 'TIMTextElem', 113 | 'MsgContent' => [ 114 | 'Text' => '晚上去撸串啊' 115 | ] 116 | ] 117 | ] 118 | ]; 119 | 120 | print_r($im->send('openim','sendmsg',$params)); 121 | ``` 122 | 123 | 返回示例: 124 | ```php 125 | { 126 | "ActionStatus":"OK", 127 | "ErrorInfo":"", 128 | "ErrorCode":0, 129 | "MsgTime":1573179125, 130 | "MsgKey":"748144182_1287657_1573179125" 131 | } 132 | ``` 133 | 134 | > 其中 `send` 方法接收三个参数。第一个参数 $servicename : 内部服务名,不同的 servicename 对应不同的服务类型;第二个参数 `$command`:命令字,与 servicename 组合用来标识具体的业务功能;第三个参数为请求包主体 135 | 136 | > 示例:`v4/im_open_login_svc/account_import`,其中 `im_open_login_svc` 为 `servicename`; `account_import` 为 `command` 137 | 138 | 请求包示例: 139 | ```php 140 | { 141 | "From_Account":"id", 142 | "ProfileItem": 143 | [ 144 | { 145 | "Tag":"Tag_Profile_IM_Nick", 146 | "Value":"MyNickName" 147 | } 148 | ] 149 | } 150 | ``` 151 | 152 | 更多用法请参考 [REST API 接口列表](https://cloud.tencent.com/document/product/269/1520) 153 | 154 | TODO 155 | 156 | ## Contributing 157 | 158 | You can contribute in one of three ways: 159 | 160 | 1. File bug reports using the [issue tracker](https://github.com/hedeqiang/hyperf-im/issues). 161 | 2. Answer questions or fix bugs on the [issue tracker](https://github.com/hedeqiang/hyperf-im/issues). 162 | 3. Contribute new features or update the wiki. 163 | 164 | _The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable._ 165 | 166 | ## License 167 | 168 | MIT 169 | -------------------------------------------------------------------------------- /src/Support/Config.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Hedeqiang\IM\Support; 13 | 14 | use ArrayAccess; 15 | use InvalidArgumentException; 16 | 17 | /** 18 | * Class Config. 19 | */ 20 | class Config implements ArrayAccess 21 | { 22 | /** 23 | * @var array 24 | */ 25 | protected $config; 26 | 27 | /** 28 | * Config constructor. 29 | */ 30 | public function __construct(array $config) 31 | { 32 | $this->config = $config; 33 | } 34 | 35 | /** 36 | * Get an item from an array using "dot" notation. 37 | * 38 | * @param string $key 39 | * @param mixed $default 40 | * 41 | * @return mixed 42 | */ 43 | public function get($key, $default = null) 44 | { 45 | $config = $this->config; 46 | if (is_null($key)) { 47 | return $config; 48 | } 49 | if (isset($config[$key])) { 50 | return $config[$key]; 51 | } 52 | foreach (explode('.', $key) as $segment) { 53 | if (!is_array($config) || !array_key_exists($segment, $config)) { 54 | return $default; 55 | } 56 | $config = $config[$segment]; 57 | } 58 | 59 | return $config; 60 | } 61 | 62 | /** 63 | * Set an array item to a given value using "dot" notation. 64 | * 65 | * @param string $key 66 | * @param mixed $value 67 | * 68 | * @return array 69 | */ 70 | public function set($key, $value) 71 | { 72 | if (is_null($key)) { 73 | throw new InvalidArgumentException('Invalid config key.'); 74 | } 75 | $keys = explode('.', $key); 76 | $config = &$this->config; 77 | while (count($keys) > 1) { 78 | $key = array_shift($keys); 79 | if (!isset($config[$key]) || !is_array($config[$key])) { 80 | $config[$key] = []; 81 | } 82 | $config = &$config[$key]; 83 | } 84 | $config[array_shift($keys)] = $value; 85 | 86 | return $config; 87 | } 88 | 89 | /** 90 | * Determine if the given configuration value exists. 91 | * 92 | * @param string $key 93 | * 94 | * @return bool 95 | */ 96 | public function has($key) 97 | { 98 | return (bool) $this->get($key); 99 | } 100 | 101 | /** 102 | * Whether a offset exists. 103 | * 104 | * @see http://php.net/manual/en/arrayaccess.offsetexists.php 105 | * 106 | * @param mixed $offset

107 | * An offset to check for. 108 | *

109 | * 110 | * @return bool true on success or false on failure. 111 | *

112 | *

113 | * The return value will be casted to boolean if non-boolean was returned 114 | * 115 | * @since 5.0.0 116 | */ 117 | public function offsetExists($offset) 118 | { 119 | return array_key_exists($offset, $this->config); 120 | } 121 | 122 | /** 123 | * Offset to retrieve. 124 | * 125 | * @see http://php.net/manual/en/arrayaccess.offsetget.php 126 | * 127 | * @param mixed $offset

128 | * The offset to retrieve. 129 | *

130 | * 131 | * @return mixed Can return all value types 132 | * 133 | * @since 5.0.0 134 | */ 135 | public function offsetGet($offset) 136 | { 137 | return $this->get($offset); 138 | } 139 | 140 | /** 141 | * Offset to set. 142 | * 143 | * @see http://php.net/manual/en/arrayaccess.offsetset.php 144 | * 145 | * @param mixed $offset

146 | * The offset to assign the value to. 147 | *

148 | * @param mixed $value

149 | * The value to set. 150 | *

151 | * 152 | * @since 5.0.0 153 | */ 154 | public function offsetSet($offset, $value) 155 | { 156 | $this->set($offset, $value); 157 | } 158 | 159 | /** 160 | * Offset to unset. 161 | * 162 | * @see http://php.net/manual/en/arrayaccess.offsetunset.php 163 | * 164 | * @param mixed $offset

165 | * The offset to unset. 166 | *

167 | * 168 | * @since 5.0.0 169 | */ 170 | public function offsetUnset($offset) 171 | { 172 | $this->set($offset, null); 173 | } 174 | } 175 | --------------------------------------------------------------------------------