chenlong 4 rokov pred
commit
1ef770e527

+ 20 - 0
.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

+ 11 - 0
.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

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+.idea
+*.DS_Store
+/vendor
+/coverage
+sftp-config.json
+composer.lock
+.subsplit
+.php_cs.cache

+ 27 - 0
.php_cs

@@ -0,0 +1,27 @@
+<?php
+$header = <<<EOF
+This file is part of the chuckpac/weather.
+
+(c) chenlong <chenlong@dalongyun.com>
+
+This source file is subject to the MIT license that is bundled.
+EOF;
+
+return PhpCsFixer\Config::create()
+    ->setRiskyAllowed(true)
+    ->setRules(array(
+        '@Symfony' => true,
+        'header_comment' => array('header' => $header),
+        'array_syntax' => array('syntax' => 'short'),
+        'ordered_imports' => true,
+        'no_useless_else' => true,
+        'no_useless_return' => true,
+        'php_unit_construct' => true,
+        'php_unit_strict' => true,
+    ))
+    ->setFinder(
+        PhpCsFixer\Finder::create()
+            ->exclude('vendor')
+            ->in(__DIR__)
+    )
+;

+ 1 - 0
.phpunit.result.cache

@@ -0,0 +1 @@
+C:37:"PHPUnit\Runner\DefaultTestResultCache":366:{a:2:{s:7:"defects";a:0:{}s:5:"times";a:4:{s:65:"Chuckpac\Weather\Tests\WeatherTest::testGetWeatherWithInvalidType";d:0.003;s:67:"Chuckpac\Weather\Tests\WeatherTest::testGetWeatherWithInvalidFormat";d:0;s:66:"Chuckpac\Weather\Tests\WeatherTest1::testGetWeatherWithInvalidType";d:0.007;s:68:"Chuckpac\Weather\Tests\WeatherTest1::testGetWeatherWithInvalidFormat";d:0;}}}

+ 230 - 0
README.md

@@ -0,0 +1,230 @@
+
+<h1 align="center">Weather</h1>
+
+<p align="center">:rainbow: 基于高德开放平台的 PHP 天气信息组件。</p>
+
+[![Build Status](https://travis-ci.org/overtrue/weather.svg?branch=master)](https://travis-ci.org/overtrue/weather) 
+[![StyleCI build status](https://github.styleci.io/repos/144818004/shield)](https://github.styleci.io/repos/144818004)
+
+## 安装
+
+```sh
+$ composer require chuckpac/weather -vvv
+```
+
+## 配置
+
+在使用本扩展之前,你需要去 [高德开放平台](https://lbs.amap.com/dev/id/newuser) 注册账号,然后创建应用,获取应用的 API Key。
+
+
+## 使用
+
+```php
+use Chuckpac\Weather\Weather;
+
+$key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx';
+
+$weather = new Weather($key);
+```
+
+###  获取实时天气
+
+```php
+$response = $weather->getLiveWeather('深圳');
+```
+示例:
+
+```json
+{
+    "status": "1",
+    "count": "1",
+    "info": "OK",
+    "infocode": "10000",
+    "lives": [
+        {
+            "province": "广东",
+            "city": "深圳市",
+            "adcode": "440300",
+            "weather": "中雨",
+            "temperature": "27",
+            "winddirection": "西南",
+            "windpower": "5",
+            "humidity": "94",
+            "reporttime": "2018-08-21 16:00:00"
+        }
+    ]
+}
+```
+
+### 获取近期天气预报
+
+```
+$response = $weather->getForecastsWeather('深圳');
+```
+示例:
+
+```json
+{
+    "status": "1", 
+    "count": "1", 
+    "info": "OK", 
+    "infocode": "10000", 
+    "forecasts": [
+        {
+            "city": "深圳市", 
+            "adcode": "440300", 
+            "province": "广东", 
+            "reporttime": "2018-08-21 11:00:00", 
+            "casts": [
+                {
+                    "date": "2018-08-21", 
+                    "week": "2", 
+                    "dayweather": "雷阵雨", 
+                    "nightweather": "雷阵雨", 
+                    "daytemp": "31", 
+                    "nighttemp": "26", 
+                    "daywind": "无风向", 
+                    "nightwind": "无风向", 
+                    "daypower": "≤3", 
+                    "nightpower": "≤3"
+                }, 
+                {
+                    "date": "2018-08-22", 
+                    "week": "3", 
+                    "dayweather": "雷阵雨", 
+                    "nightweather": "雷阵雨", 
+                    "daytemp": "32", 
+                    "nighttemp": "27", 
+                    "daywind": "无风向", 
+                    "nightwind": "无风向", 
+                    "daypower": "≤3", 
+                    "nightpower": "≤3"
+                }, 
+                {
+                    "date": "2018-08-23", 
+                    "week": "4", 
+                    "dayweather": "雷阵雨", 
+                    "nightweather": "雷阵雨", 
+                    "daytemp": "32", 
+                    "nighttemp": "26", 
+                    "daywind": "无风向", 
+                    "nightwind": "无风向", 
+                    "daypower": "≤3", 
+                    "nightpower": "≤3"
+                }, 
+                {
+                    "date": "2018-08-24", 
+                    "week": "5", 
+                    "dayweather": "雷阵雨", 
+                    "nightweather": "雷阵雨", 
+                    "daytemp": "31", 
+                    "nighttemp": "26", 
+                    "daywind": "无风向", 
+                    "nightwind": "无风向", 
+                    "daypower": "≤3", 
+                    "nightpower": "≤3"
+                }
+            ]
+        }
+    ]
+}
+```
+
+### 获取 XML 格式返回值
+
+以上两个方法第二个参数为返回值类型,可选 `json` 与 `xml`,默认 `json`:
+
+```php
+$response = $weather->getLiveWeather('深圳', 'xml');
+```
+
+示例:
+
+```xml
+<response>
+    <status>1</status>
+    <count>1</count>
+    <info>OK</info>
+    <infocode>10000</infocode>
+    <lives type="list">
+        <live>
+            <province>广东</province>
+            <city>深圳市</city>
+            <adcode>440300</adcode>
+            <weather>中雨</weather>
+            <temperature>27</temperature>
+            <winddirection>西南</winddirection>
+            <windpower>5</windpower>
+            <humidity>94</humidity>
+            <reporttime>2018-08-21 16:00:00</reporttime>
+        </live>
+    </lives>
+</response>
+```
+
+### 参数说明
+
+```
+array | string   getLiveWeather(string $city, string $format = 'json')
+array | string   getForecastsWeather(string $city, string $format = 'json')
+```
+
+> - `$city` - 城市名/[高德地址位置 adcode](https://lbs.amap.com/api/webservice/guide/api/district),比如:“深圳” 或者(adcode:440300);
+> - `$format`  - 输出的数据格式,默认为 json 格式,当 output 设置为 “`xml`” 时,输出的为 XML 格式的数据。
+
+
+### 在 Laravel 中使用
+
+在 Laravel 中使用也是同样的安装方式,配置写在 `config/services.php` 中:
+
+```php
+    .
+    .
+    .
+     'weather' => [
+        'key' => env('WEATHER_API_KEY'),
+    ],
+```
+
+然后在 `.env` 中配置 `WEATHER_API_KEY` :
+
+```env
+WEATHER_API_KEY=xxxxxxxxxxxxxxxxxxxxx
+```
+
+可以用两种方式来获取 `Chuckpac\Weather\Weather` 实例:
+
+#### 方法参数注入
+
+```php
+    .
+    .
+    .
+    public function edit(Weather $weather) 
+    {
+        $response = $weather->getLiveWeather('深圳');
+    }
+    .
+    .
+    .
+```
+
+#### 服务名访问
+
+```php
+    .
+    .
+    .
+    public function edit() 
+    {
+        $response = app('weather')->getLiveWeather('深圳');
+    }
+    .
+    .
+    .
+
+```
+
+## 参考
+
+- [高德开放平台天气接口](https://lbs.amap.com/api/webservice/guide/api/weatherinfo/)

+ 30 - 0
composer.json

@@ -0,0 +1,30 @@
+{
+    "name": "chuckpac\/weather",
+    "description": "A weather SDK",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "chenlong",
+            "email": "chenlong@dalongyun.com"
+        }
+    ],
+    "require": {
+        "guzzlehttp/guzzle": "^7.2"
+    },
+    "autoload": {
+        "psr-4": {
+            "Chuckpac\\Weather\\": "src"
+        }
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~5 || ~7",
+        "mockery/mockery": "^1.1"
+    },
+    "extra": {
+        "laravel": {
+            "providers": [
+                "Chuckpac\\Weather\\ServiceProvider"
+            ]
+        }
+    }
+}

+ 24 - 0
index.php

@@ -0,0 +1,24 @@
+<?php
+
+require __DIR__ .'/vendor/autoload.php';
+
+use Chuckpac\Weather\Weather;
+
+// 高德开放平台应用 API Key
+$key = 'bb5e3bd493d1f29f52f9d8ee4bf47049';
+$w = new Weather($key);
+
+echo "获取实时天气:\n";
+
+$response = $w->getWeather('深圳');
+echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+
+echo "\n\n获取天气预报:\n";
+
+$response = $w->getWeather('深圳', 'all');
+echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+
+
+echo "\n\n获取实时天气(XML):\n";
+
+echo $w->getWeather('深圳', 'base', 'xml');

+ 21 - 0
phpunit.xml.dist

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         bootstrap="vendor/autoload.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false">
+    <testsuites>
+        <testsuite name="Application Test Suite">
+            <directory>./tests/</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist>
+            <directory suffix=".php">src/</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 0 - 0
src/.gitkeep


+ 13 - 0
src/Exceptions/Exception.php

@@ -0,0 +1,13 @@
+<?php
+
+
+namespace Chuckpac\Weather\Exceptions;
+
+/**
+ * Class Exception
+ * @package Chuckpac\Weather\Exceptions
+ */
+class Exception extends \Exception
+{
+
+}

+ 14 - 0
src/Exceptions/HttpException.php

@@ -0,0 +1,14 @@
+<?php
+
+
+namespace Chuckpac\Weather\Exceptions;
+
+/**
+ * http request exceptions
+ * Class HttpException
+ * @package Chuckpac\Weather\Exceptions
+ */
+class HttpException extends Exception
+{
+
+}

+ 14 - 0
src/Exceptions/InvalidArgumentException.php

@@ -0,0 +1,14 @@
+<?php
+
+
+namespace Chuckpac\Weather\Exceptions;
+
+/**
+ * invalid arguments exceptions
+ * Class InvalidArgumentException
+ * @package Chuckpac\Weather\Exceptions
+ */
+class InvalidArgumentException extends Exception
+{
+
+}

+ 24 - 0
src/ServiceProvider.php

@@ -0,0 +1,24 @@
+<?php
+
+
+namespace Chuckpac\Weather;
+
+
+class ServiceProvider extends \Illuminate\Support\ServiceProvider
+{
+    protected $defer = true;
+
+    public function register()
+    {
+        $this->app->singleton(Weather::class, function(){
+            return new Weather(config('services.weather.key'));
+        });
+
+        $this->app->alias(Weather::class, 'weather');
+    }
+
+    public function provides()
+    {
+        return [Weather::class, 'weather'];
+    }
+}

+ 91 - 0
src/Weather.php

@@ -0,0 +1,91 @@
+<?php
+
+
+namespace Chuckpac\Weather;
+
+use GuzzleHttp\Client;
+use Chuckpac\Weather\Exceptions\HttpException;
+use Chuckpac\Weather\Exceptions\InvalidArgumentException;
+
+class Weather
+{
+    /**
+     * GaoDe Weather Api key
+     * @var String
+     */
+    protected $key;
+
+    /**
+     * guzzle client params
+     * @var array
+     */
+    protected $guzzleOptions = [];
+
+    /**
+     * Weather constructor.
+     * @param string $key
+     */
+    public function __construct(string $key)
+    {
+        $this->key = $key;
+    }
+
+    /**
+     * get http client object
+     * @return Client
+     */
+    public function getHttpClient()
+    {
+        return new Client($this->guzzleOptions);
+    }
+
+    /**
+     * set guzzle options
+     * @param array $options
+     */
+    public function setGuzzleOptions(array $options)
+    {
+        $this->guzzleOptions = $options;
+    }
+
+    /**
+     * get weather info
+     * @param $city
+     * @param string $type
+     * @param string $format
+     * @return mixed|string
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function getWeather($city,$type = 'base',$format = 'json')
+    {
+        $url = 'https://restapi.amap.com/v3/weather/weatherInfo';
+
+        if(!\in_array(\strtolower($format),['json','xml'])){
+            throw new InvalidArgumentException('Invalid response format: '.$format);
+        }
+
+        if(!\in_array(\strtolower($type),['base','all'])){
+            throw new InvalidArgumentException('Invalid type value(base/all): '.$type);
+        }
+
+        $query = array_filter([
+            'key' => $this->key,
+            'city' => $city,
+            'output' => $format,
+            'extensions' =>  $type,
+        ]);
+
+        try {
+            $response = $this->getHttpClient()->get($url, [
+                'query' => $query,
+            ])->getBody()->getContents();
+        }catch(\Exception $e){
+            throw new HttpException($e->getMessage(),$e->getCode(),$e);
+        }
+        /**
+         * json_decode true返回数组 false返回对象
+         */
+        return 'json' === $format?\json_decode($response,true):$response;
+    }
+
+}

+ 0 - 0
tests/.gitkeep


+ 147 - 0
tests/WeatherTest.php

@@ -0,0 +1,147 @@
+<?php
+
+
+namespace Chuckpac\Weather\Tests;
+
+use Chuckpac\Weather\Weather;
+use Chuckpac\Weather\Exceptions\InvalidArgumentException;
+use Chuckpac\Weather\Exceptions\HttpException;
+use PHPUnit\Framework\TestCase;
+use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\Client;
+use GuzzleHttp\ClientInterface;
+use Mockery\Matcher\AnyArgs;
+
+/**
+ * Class WeatherTest
+ * @package Weather
+ */
+class WeatherTest extends TestCase
+{
+    /**
+     * test $type
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function testGetWeatherWithInvalidType(){
+
+        $w = new Weather('key123');
+
+        //断言会抛出参数错误类
+        $this->expectException(InvalidArgumentException::class);
+
+        //断言会抛出此信息
+        $this->expectExceptionMessage('Invalid type value(base/all): foo');
+
+        $w->getWeather('江苏','foo');
+
+        $this->fail('Failed to assert getWeather throw exception with invalid argument.');
+    }
+
+    /**
+     * test $format
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function testGetWeatherWithInvalidFormat(){
+
+        $w = new Weather('key123');
+
+        //断言会抛出参数错误类
+        $this->expectException(InvalidArgumentException::class);
+
+        //断言会抛出此信息
+        $this->expectExceptionMessage('Invalid response format: array');
+
+        $w->getWeather('江苏','json','array');
+
+        $this->fail('Failed to assert getWeather throw exception with invalid argument.');
+    }
+
+    /**
+     * 模拟测试获取天气方法
+     */
+    public function testGetWeather()
+    {
+        // json
+        $response = new Response(200, [], '{"success": true}');
+        $client = \Mockery::mock(Client::class);
+        $client->allows()->get('https://restapi.amap.com/v3/weather/weatherInfo', [
+            'query' => [
+                'key' => 'mock-key',
+                'city' => '深圳',
+                'output' => 'json',
+                'extensions' => 'base',
+            ],
+        ])->andReturn($response);
+
+        $w = \Mockery::mock(Weather::class, ['mock-key'])->makePartial();
+        $w->allows()->getHttpClient()->andReturn($client);
+
+        $this->assertSame(['success' => true], $w->getWeather('深圳'));
+
+        // xml
+        $response = new Response(200, [], '<hello>content</hello>');
+        $client = \Mockery::mock(Client::class);
+        $client->allows()->get('https://restapi.amap.com/v3/weather/weatherInfo', [
+            'query' => [
+                'key' => 'mock-key',
+                'city' => '深圳',
+                'extensions' => 'all',
+                'output' => 'xml',
+            ],
+        ])->andReturn($response);
+
+        $w = \Mockery::mock(Weather::class, ['mock-key'])->makePartial();
+        $w->allows()->getHttpClient()->andReturn($client);
+
+        $this->assertSame('<hello>content</hello>', $w->getWeather('深圳', 'all', 'xml'));
+    }
+
+    /**
+     * 测试获取天气接口异常
+     */
+    public function testGetWeatherWithGuzzleRuntimeException()
+    {
+        $client = \Mockery::mock(Client::class);
+        $client->allows()
+            ->get(new AnyArgs()) // 由于上面的用例已经验证过参数传递,所以这里就不关心参数了。
+            ->andThrow(new \Exception('request timeout')); // 当调用 get 方法时会抛出异常。
+
+        $w = \Mockery::mock(Weather::class, ['mock-key'])->makePartial();
+        $w->allows()->getHttpClient()->andReturn($client);
+
+        // 接着需要断言调用时会产生异常。
+        $this->expectException(HttpException::class);
+        $this->expectExceptionMessage('request timeout');
+
+        $w->getWeather('深圳');
+    }
+
+    /**
+     * 测试是否获取到了client对象
+     */
+    public function testGetHttpClient()
+    {
+        $w = new Weather('mock-key');
+
+        // 断言返回结果为 GuzzleHttp\ClientInterface 实例
+        $this->assertInstanceOf(ClientInterface::class, $w->getHttpClient());
+    }
+
+    /**
+     * 测试是否参数设置成功
+     */
+    public function testSetGuzzleOptions()
+    {
+        $w = new Weather('mock-key');
+
+        // 设置参数前,timeout 为 null
+        $this->assertNull($w->getHttpClient()->getConfig('timeout'));
+
+        // 设置参数
+        $w->setGuzzleOptions(['timeout' => 5000]);
+
+        // 设置参数后,timeout 为 5000
+        $this->assertSame(5000, $w->getHttpClient()->getConfig('timeout'));
+    }
+
+}