From 655c8148a3a4835de664c05689e5121dddb8573b Mon Sep 17 00:00:00 2001 From: overtrue Date: Thu, 9 Mar 2017 21:27:47 +0800 Subject: [PATCH] Init commit. --- .editorconfig | 20 +++ .gitattributes | 11 ++ .gitignore | 8 ++ .php_cs | 23 ++++ README.md | 17 +++ composer.json | 30 +++++ phpunit.xml.dist | 22 +++ src/Contracts/GatewayInterface.php | 20 +++ src/EasySms.php | 208 +++++++++++++++++++++++++++++ src/Gateways/LogGateway.php | 25 ++++ src/Gateways/YunPianGateway.php | 25 ++++ src/Support/Config.php | 143 ++++++++++++++++++++ tests/EasySmsTest.php | 79 +++++++++++ tests/Support/ConfigTest.php | 61 +++++++++ tests/TestCase.php | 20 +++ 15 files changed, 712 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .php_cs create mode 100644 README.md create mode 100644 composer.json create mode 100644 phpunit.xml.dist create mode 100644 src/Contracts/GatewayInterface.php create mode 100644 src/EasySms.php create mode 100644 src/Gateways/LogGateway.php create mode 100644 src/Gateways/YunPianGateway.php create mode 100644 src/Support/Config.php create mode 100644 tests/EasySmsTest.php create mode 100644 tests/Support/ConfigTest.php create mode 100644 tests/TestCase.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..df55cd7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false + +[*.{vue,js,scss}] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9af3157 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto + +/tests export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.scrutinizer.yml export-ignore +.travis.yml export-ignore +phpunit.php export-ignore +phpunit.xml.dist export-ignore +phpunit.xml export-ignore +.php_cs export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b335fba --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.DS_Store +/vendor +/coverage +sftp-config.json +composer.lock +.subsplit +.php_cs.cache +.idea diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..744301b --- /dev/null +++ b/.php_cs @@ -0,0 +1,23 @@ + +This source file is subject to the MIT license that is bundled +with this source code in the file LICENSE. +EOF; +Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header); +return Symfony\CS\Config\Config::create() + // use default SYMFONY_LEVEL and extra fixers: + ->fixers(array( + 'header_comment', + 'short_array_syntax', + 'ordered_use', + 'php_unit_construct', + 'php_unit_strict', + )) + ->finder( + Symfony\CS\Finder\DefaultFinder::create() + ->exclude('vendor') + ->in(__DIR__) + ) +; \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9417f5b --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Easy-sms + +The easiest way to send short message. + +# Usage + +```php +use Overtrue\EasySms\EasySms; + +$config = [...]; +$easySms = new EasySms($config); +$easySms->gateway('Log')->send(1888888888, 'hello world!'); +``` + +# License + +MIT diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..82a003e --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "overtrue/easy-sms", + "description": "The easiest way to send short message.", + "type": "library", + "require": { + "guzzlehttp/guzzle": "^6.2", + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^6.0", + "mockery/mockery": "^0.9.9" + }, + "autoload": { + "psr-4": { + "Overtrue\\EasySms\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Overtrue\\EasySms\\Tests\\": "tests" + } + }, + "license": "MIT", + "authors": [ + { + "name": "overtrue", + "email": "i@overtrue.me" + } + ] +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..264baab --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + ./tests/ + + + + + src/ + + + diff --git a/src/Contracts/GatewayInterface.php b/src/Contracts/GatewayInterface.php new file mode 100644 index 0000000..9f1aef4 --- /dev/null +++ b/src/Contracts/GatewayInterface.php @@ -0,0 +1,20 @@ +config = new Config($config); + + if (!empty($config['default'])) { + $this->setDefaultGateway($config['default']); + } + } + + /** + * Create a gateway. + * + * @param string $name + * + * @return \Overtrue\EasySms\Contracts\GatewayInterface + */ + public function gateway($name = null) + { + $name = $name ?: $this->getDefaultGateway(); + + if (!isset($this->gateways[$name])) { + $this->gateways[$name] = $this->createGateway($name); + } + + return $this->gateways[$name]; + } + + /** + * Register a custom driver creator Closure. + * + * @param string $name + * @param \Closure $callback + * + * @return $this + */ + public function extend($name, Closure $callback) + { + $this->customCreators[$name] = $callback; + + return $this; + } + + /** + * Get default gateway name. + * + * @return string + * + * @throws if no default gateway configured. + */ + public function getDefaultGateway() + { + if (empty($this->defaultGateway)) { + throw new RuntimeException('No default gateway configured.'); + } + + return $this->defaultGateway; + } + + /** + * Set default gateway name. + * + * @param string $name + * + * @return $this + */ + public function setDefaultGateway($name) + { + $this->defaultGateway = $name; + + return $this; + } + + /** + * Create a new driver instance. + * + * @param string $name + * @throws \InvalidArgumentException + * + * @return mixed + */ + protected function createGateway($name) + { + if (isset($this->customCreators[$name])) { + $gateway = $this->callCustomCreator($name); + } else { + $name = $this->formatGatewayClassName($name); + $gateway = $this->makeGateway($name, $this->config->get($name, [])); + } + + if (!($gateway instanceof GatewayInterface)) { + throw new InvalidArgumentException(sprintf('Gateway "%s" not inherited from %s.', $name, GatewayInterface::class)); + } + + return $gateway; + } + + /** + * Make gateway instance. + * + * @param string $gateway + * @param array $config + * + * @return \Overtrue\EasySms\Contracts\GatewayInterface + */ + protected function makeGateway($gateway, $config) + { + if (!class_exists($gateway)) { + throw new InvalidArgumentException(sprintf('Gateway "%s" not exists.', $gateway)); + } + + return new $gateway($config); + } + + /** + * Format gateway name. + * + * @param string $name + * + * @return string + */ + protected function formatGatewayClassName($name) + { + if (class_exists($name)) { + return $name; + } + + $name = $this->camelCase($name); + + return __NAMESPACE__."\\Gateways\\{$name}Gateway"; + } + + /** + * Call a custom gateway creator. + * + * @param string gateway + * + * @return mixed + */ + protected function callCustomCreator($gateway) + { + return call_user_func($this->customCreators[$gateway], $this->config->get($gateway, [])); + } + + /** + * Convert string to camel case. + * + * @param string $name + * + * @return string + */ + protected function camelCase($name) + { + return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $name))); + } + + /** + * Dynamically call the default gateway instance. + * + * @param string $method + * @param array $parameters + * + * @return mixed + */ + public function __call($method, $parameters) + { + return call_user_func_array([$this->gateway(), $method], $parameters); + } +} \ No newline at end of file diff --git a/src/Gateways/LogGateway.php b/src/Gateways/LogGateway.php new file mode 100644 index 0000000..ae1e295 --- /dev/null +++ b/src/Gateways/LogGateway.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Support; + +use ArrayAccess; +use InvalidArgumentException; + +/** + * Class Config. + */ +class Config implements ArrayAccess +{ + /** + * @var array + */ + protected $config; + + /** + * Config constructor. + * + * @param array $config + */ + public function __construct(array $config) + { + $this->config = $config; + } + + /** + * Get an item from an array using "dot" notation. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function get($key, $default = null) + { + $config = $this->config; + + if (is_null($key)) { + return null; + } + + if (isset($config[$key])) { + return $config[$key]; + } + foreach (explode('.', $key) as $segment) { + if (!is_array($config) || !array_key_exists($segment, $config)) { + return $default; + } + $config = $config[$segment]; + } + + return $config; + } + + /** + * Whether a offset exists. + * + * @link http://php.net/manual/en/arrayaccess.offsetexists.php + * + * @param mixed $offset

+ * An offset to check for. + *

+ * + * @return bool true on success or false on failure. + *

+ *

+ * The return value will be casted to boolean if non-boolean was returned + * + * @since 5.0.0 + */ + public function offsetExists($offset) + { + return array_key_exists($offset, $this->config); + } + + /** + * Offset to retrieve. + * + * @link http://php.net/manual/en/arrayaccess.offsetget.php + * + * @param mixed $offset

+ * The offset to retrieve. + *

+ * + * @return mixed Can return all value types + * + * @since 5.0.0 + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Offset to set. + * + * @link http://php.net/manual/en/arrayaccess.offsetset.php + * + * @param mixed $offset

+ * The offset to assign the value to. + *

+ * @param mixed $value

+ * The value to set. + *

+ * + * @since 5.0.0 + */ + public function offsetSet($offset, $value) + { + if (isset($this->config[$offset])) { + $this->config[$offset] = $value; + } + } + + /** + * Offset to unset. + * + * @link http://php.net/manual/en/arrayaccess.offsetunset.php + * + * @param mixed $offset

+ * The offset to unset. + *

+ * + * @since 5.0.0 + */ + public function offsetUnset($offset) + { + if (isset($this->config[$offset])) { + unset($this->config[$offset]); + } + } +} \ No newline at end of file diff --git a/tests/EasySmsTest.php b/tests/EasySmsTest.php new file mode 100644 index 0000000..8b26ffe --- /dev/null +++ b/tests/EasySmsTest.php @@ -0,0 +1,79 @@ +assertInstanceOf(GatewayInterface::class, $easySms->gateway('Log')); + + // invalid gateway + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Gateway "Overtrue\EasySms\Gateways\NotExistsGatewayNameGateway" not exists.'); + + $easySms->gateway('NotExistsGatewayName'); + } + + public function testGatewayWithoutDefaultSetting() + { + $easySms = new EasySms([]); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('No default gateway configured.'); + + $easySms->gateway(); + } + + public function testGatewayWithDefaultSetting() + { + $easySms = new EasySms(['default' => DummyGatewayForTest::class]); + $this->assertSame(DummyGatewayForTest::class, $easySms->getDefaultGateway()); + $this->assertInstanceOf(DummyGatewayForTest::class, $easySms->gateway()); + + // invalid gateway + $easySms->setDefaultGateway(DummyInvalidGatewayForTest::class); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + sprintf('Gateway "%s" not inherited from %s.', + DummyInvalidGatewayForTest::class, + GatewayInterface::class) + ); + $easySms->gateway(); + } + + public function testExtend() + { + $easySms = new EasySms([]); + $easySms->extend('foo', function() { + return new DummyGatewayForTest(); + }); + + $this->assertInstanceOf(DummyGatewayForTest::class, $easySms->gateway('foo')); + } + + public function testMagicCall() + { + $easySms = new EasySms(['default' => DummyGatewayForTest::class]); + + $this->assertSame('send-result', $easySms->send('mock-number', 'hello')); + } +} + +class DummyGatewayForTest implements GatewayInterface { + public function send() + { + return 'send-result'; + } +} + +class DummyInvalidGatewayForTest { + // nothing +} diff --git a/tests/Support/ConfigTest.php b/tests/Support/ConfigTest.php new file mode 100644 index 0000000..93159aa --- /dev/null +++ b/tests/Support/ConfigTest.php @@ -0,0 +1,61 @@ + 'bar', + 'bar' => [ + 'screen_name' => 'somebody', + 'profile' => [ + 'id' => 9999, + 'name' => 'overtrue', + ] + ], + 'numbers' => [ + [ + 'id' => 1, + 'number' => 1 + ], + [ + 'id' => 2, + 'number' => 2 + ], + ], + ]); + + $this->assertTrue(isset($config['foo'])); + + $this->assertSame('bar', $config['foo']); + $this->assertSame('bar', $config->get('foo')); + $this->assertNull($config->get(null)); + + $this->assertSame(9999, $config->get('bar.profile.id')); + $this->assertSame('overtrue', $config->get('bar.profile.name')); + + $this->assertSame(1, $config->get('numbers.0.id')); + $this->assertSame(1, $config->get('numbers.0.number')); + + $this->assertSame(2, $config->get('numbers.1.id')); + $this->assertSame(2, $config->get('numbers.1.number')); + + $config['foo'] = 'new-bar'; + $this->assertSame('new-bar', $config['foo']); + + unset($config['foo']); + $this->assertNull($config['foo']); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..66abdc9 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,20 @@ +addToAssertionCount($container->mockery_getExpectationCount()); + } + + Mockery::close(); + } +} \ No newline at end of file