├── .gitignore ├── CHANGELOG.md ├── CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── composer.json ├── phpunit.xml ├── resources └── images │ ├── microservices-communication.png │ └── middlewares-chain.png ├── src ├── Abstracts │ ├── MicroClientAbstract.php │ └── MiddlewareAbstract.php ├── Classes │ ├── Bag.php │ ├── Request.php │ └── Response.php ├── Console │ ├── Commands │ │ ├── MicroClientMakeCommand.php │ │ └── MicroClientMiddlewareMakeCommand.php │ └── stubs │ │ ├── client.stub │ │ └── middleware.stub ├── Contracts │ ├── MicroClientInterface.php │ ├── MiddlewareInterface.php │ ├── RequestInterface.php │ └── ResponseInterface.php ├── Middlewares │ ├── Cache.php │ ├── CacheMiddleware.php │ └── Middleware.php ├── Providers │ └── ExtractorServiceProvider.php └── Traits │ ├── Conditional.php │ ├── HasMiddleware.php │ ├── HasParsedUri.php │ └── HttpURL.php └── tests └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | 4 | # ignore phpstorm cache files 5 | .idea -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All Notable changes to `extractor` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 6 | 7 | ## Date - 2019-01-09 8 | 9 | ### Fixed 10 | - Nothing 11 | 12 | ### Added 13 | - Nothing 14 | 15 | ### Deprecated 16 | - Nothing 17 | 18 | ### Fixed 19 | - Nothing 20 | 21 | ### Removed 22 | - Nothing 23 | 24 | ### Security 25 | - Nothing 26 | -------------------------------------------------------------------------------- /CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at `khanzadimahdi@gmail.com`. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/shetabit/extractor). 6 | 7 | ## Pull Requests 8 | 9 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``. 10 | 11 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 12 | 13 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 14 | 15 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 16 | 17 | - **Create feature branches** - Don't ask us to pull from your master branch. 18 | 19 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 20 | 21 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 22 | 23 | 24 | **Happy coding**! 25 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Detailed description 4 | 5 | Provide a detailed description of the change or addition you are proposing. 6 | 7 | Make it clear if the issue is a bug, an enhancement or just a question. 8 | 9 | ## Context 10 | 11 | Why is this change important to you? How would you use it? 12 | 13 | How can it benefit other users? 14 | 15 | ## Possible implementation 16 | 17 | Not obligatory, but suggest an idea for implementing addition or change. 18 | 19 | ## Your environment 20 | 21 | Include as many relevant details about the environment you experienced the bug in and how to reproduce it. 22 | 23 | * Version used (e.g. PHP 7.2, HHVM 3): 24 | * Operating system and version (e.g. Ubuntu 16.04, Windows 7): 25 | * Link to your project: 26 | * ... 27 | * ... 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mahdi Khanzadi 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 13 | > all 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 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | Describe your changes in detail. 6 | 7 | ## Motivation and context 8 | 9 | Why is this change required? What problem does it solve? 10 | 11 | If it fixes an open issue, please link to the issue here (if you write `fixes #num` 12 | or `closes #num`, the issue will be automatically closed when the pull is accepted.) 13 | 14 | ## How has this been tested? 15 | 16 | Please describe in detail how you tested your changes. 17 | 18 | Include details of your testing environment, and the tests you ran to 19 | see how your change affects other areas of the code, etc. 20 | 21 | ## Screenshots (if appropriate) 22 | 23 | ## Types of changes 24 | 25 | What types of changes does your code introduce? Put an `x` in all the boxes that apply: 26 | - [ ] Bug fix (non-breaking change which fixes an issue) 27 | - [ ] New feature (non-breaking change which adds functionality) 28 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 29 | 30 | ## Checklist: 31 | 32 | Go over all the following points, and put an `x` in all the boxes that apply. 33 | 34 | Please, please, please, don't send your pull request until all of the boxes are ticked. Once your pull request is created, it will trigger a build on our [continuous integration](http://www.phptherightway.com/#continuous-integration) server to make sure your [tests and code style pass](https://help.github.com/articles/about-required-status-checks/). 35 | 36 | - [ ] I have read the **[CONTRIBUTING](CONTRIBUTING.md)** document. 37 | - [ ] My pull request addresses exactly one patch/feature. 38 | - [ ] I have created a branch for this patch/feature. 39 | - [ ] Each individual commit in the pull request is meaningful. 40 | - [ ] I have added tests to cover my changes. 41 | - [ ] If my change requires a change to the documentation, I have updated it accordingly. 42 | 43 | If you're unsure about any of these, don't hesitate to ask. We're here to help! 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Laravel Extractor 6 | 7 | Communicate with **remote servers** or **microservices** in an easy way. 8 | 9 | All requests and responses can be **cached** and **manipulated** on runtime using **middlewares**. 10 | 11 | [Donate me](https://yekpay.me/mahdikhanzadi) if you like this package :sunglasses: :bowtie: 12 | 13 | ## List of contents 14 | 15 | - [Install](#install) 16 | - [How to use](#how-to-use) 17 | - [Send requests](#send-requests) 18 | - [Send concurrent requests](#send-concurrent-requests) 19 | - [Event listeners](#event-listeners) 20 | - [Middlewares](#middlewares) 21 | - [How to create](#how-to-create) 22 | - [Global middlewares](#global-middlewares) 23 | - [Cache](#cache) 24 | - [Conditional configs](#conditional-configs) 25 | - [Clients](#Clients) 26 | - [Create clients](#create-clients) 27 | - [Run a client](#run-a-client) 28 | - [Send requests](#send-requests) 29 | - [Send concurrent requests](#send-concurrent-requests) 30 | - [Change log](#change-log) 31 | - [Contributing](#contributing) 32 | - [Security](#security) 33 | - [Credits](#credits) 34 | - [License](#license) 35 | 36 | ## Install 37 | 38 | Via Composer 39 | 40 | ```bash 41 | $ composer require shetabit/extractor 42 | ``` 43 | 44 | If you are using `Laravel 5.5` or higher then you don't need to add the provider and alias. 45 | 46 | In your `config/app.php` file add below lines. 47 | 48 | ```php 49 | # In your providers array. 50 | 'providers' => [ 51 | ... 52 | Shetabit\Extractor\Providers\ExtractorServiceProvider::class, 53 | ] 54 | ``` 55 | 56 | ## How to use 57 | 58 | #### Send requests 59 | 60 | you can send requests to remote API using `Request` class, see the below example: 61 | 62 | ```php 63 | // at the top 64 | use Shetabit\Extractor\Classes\Request; 65 | 66 | //... 67 | 68 | // create new request 69 | $request = new Request(); 70 | 71 | // set api's url and method 72 | $request->setUri($url)->setMethod('get'); 73 | 74 | // run the request and get data 75 | $response = $request->fetch(); 76 | 77 | var_dump($response); // show given response 78 | ``` 79 | 80 | as you see, you can work with remote API in an easy way. 81 | 82 | the `Request` has more methods to add `fields`, `headers` and etc. 83 | 84 | ```php 85 | use Shetabit\Extractor\Classes\Request; 86 | 87 | //... 88 | $request = new Request(); 89 | 90 | 91 | # Example 1: 92 | $request 93 | ->setUri('http://your-site.com') 94 | ->setMethod('post') 95 | // add some headers 96 | ->addHeader('Authorization', "Bearer dfaerfaeaeva1351adsfaecva") 97 | ->addHeader('Accept', 'application/json') 98 | // add form parameters 99 | ->addFormParam('email', $email) 100 | ->addFormParam('password', $password); 101 | 102 | $response = $request->fetch(); // run request 103 | 104 | 105 | # Example 2: 106 | $request 107 | ->setUri('http://your-site.com') 108 | ->setMethod('get') 109 | // add query string 110 | ->addQuery('page', $page) 111 | ->addQuery('s', $search); 112 | 113 | $response = $request->fetch(); // run request 114 | ``` 115 | 116 | #### Send concurrent requests 117 | 118 | you can send concurrent requests like the below 119 | 120 | ```php 121 | use Shetabit\Extractor\Classes\Request; 122 | use Shetabit\Extractor\Contracts\RequestInterface; 123 | 124 | // ... 125 | 126 | $request = new Request; 127 | 128 | $responses = $request 129 | ->createBag() 130 | ->addRequest(function(RequestInterface $request) { 131 | $request->setUri('http://google.com/'); 132 | }) 133 | ->addRequest(function(RequestInterface $request) { 134 | $request->setUri('http://bing.com/'); 135 | }) 136 | ->fetch(); 137 | ``` 138 | 139 | #### Event listeners 140 | 141 | you can set `success` and `error` listener for each requests seperately. here is another example that uses `onSuccess` and `onError` listeners. 142 | 143 | ```php 144 | use Shetabit\Extractor\Classes\Request; 145 | use Shetabit\Extractor\Contracts\RequestInterface; 146 | 147 | // ... 148 | 149 | $request = new Request; 150 | 151 | # Example 1: using on success 152 | $response = $request 153 | ->setUri('http://google.com/') 154 | ->onSuccess(function (ResponseInterface $response, RequestInterface $request) { 155 | echo $response->getBody(); 156 | }) 157 | ->fetch(); 158 | 159 | 160 | # Example 2: using on error 161 | $response = $request 162 | ->setUri('http://yahoo.com/') 163 | ->onSuccess(function (ResponseInterface $response, RequestInterface $request) { 164 | echo 'success'; 165 | }) 166 | ->onError(function (ResponseInterface $response, RequestInterface $request) { 167 | echo 'fail'; 168 | }); 169 | 170 | 171 | # Example 3: using request's bag 172 | $response = $request 173 | ->createBag() 174 | ->addRequest(function (RequestInterface $request) { 175 | $request 176 | ->setUri('http://google.com/') 177 | ->onSuccess(function (ResponseInterface $response, RequestInterface $request) { 178 | echo $response->getBody(); 179 | }); 180 | }) 181 | ->addRequest(function (RequestInterface $request) { 182 | $request 183 | ->setUri('http://yahoo.com/') 184 | ->onSuccess(function (ResponseInterface $response, RequestInterface $request) { 185 | echo 'success'; 186 | }) 187 | ->onError(function (ResponseInterface $response, RequestInterface $request) { 188 | echo 'fail'; 189 | }); 190 | }) 191 | ->fetch(); 192 | ``` 193 | 194 | #### Middlewares 195 | 196 |

197 | 198 |

199 | 200 | 201 | 202 | ##### How to create 203 | 204 | Middlewares can be created by running the below command 205 | 206 | ```shell 207 | php artisan make:extractor-middleware test 208 | ``` 209 | 210 | The former command will create a middleware named `test` in `app\Http\RemoteRequests\Middlewares` path. 211 | 212 | You can add a middleware to request like the below: 213 | 214 | ```php 215 | $request 216 | ->setUri('http://your-site.com') 217 | ->setMethod('get') 218 | ->middleware(new AuthMiddleware) 219 | ->fetch(); 220 | ``` 221 | 222 | Multiple middlewares can be used by calling `middleware` method multiple times: 223 | 224 | ```php 225 | $request 226 | ->setUri('http://your-site.com') 227 | ->setMethod('get') 228 | ->middleware(new Test1) 229 | ->middleware(new Test2) 230 | ->fetch(); 231 | ``` 232 | 233 | Each middleware has a `handle` method that can be used to handle requests and responses. 234 | 235 | The following middleware would perform some task before the request is handled by the application: 236 | 237 | ```php 238 | public function handle($request, Closure $next) { 239 | if($user->name == 'john') { 240 | $request->addQuery('name', 'john'); 241 | } 242 | 243 | return $next($request); 244 | } 245 | ``` 246 | 247 | However, this middleware would perform its task after the request is handled by the application: 248 | 249 | ```php 250 | public function handle($request, Closure $next) 251 | { 252 | $response = $next($request); 253 | 254 | // Perform action 255 | 256 | return $response; 257 | } 258 | ``` 259 | 260 | ##### Global middlewares 261 | 262 | You can use `Request::withGlobalMiddlewares` to add global middlewares. 263 | global middlewares will be binded to all requests. 264 | 265 | ```php 266 | 267 | // in your AppServiceProvider 268 | 269 | protected boot() 270 | { 271 | Request::withGlobalMiddlewares([ 272 | // list of middlewares 273 | ]); 274 | } 275 | 276 | ``` 277 | 278 | in each request, you can unbind global middlewares, if you need them just use `withoutMiddleware` like the below: 279 | 280 | ```php 281 | // at the top 282 | use Shetabit\Extractor\Classes\Request; 283 | 284 | $url = 'http://google.com/'; 285 | 286 | $response = (new Request) 287 | ->setUri($url) 288 | ->withoutMiddleware(new TestMiddleware) 289 | ->fetch(); 290 | ``` 291 | 292 | 293 | ##### Cache 294 | 295 | you can cache responses according to requests. 296 | 297 | ```php 298 | // at the top 299 | use Shetabit\Extractor\Classes\Request; 300 | 301 | $url = 'http://google.com/'; 302 | $ttl = 5; // 5 seconds 303 | 304 | $response = (new Request)->setUri($url)->cache($ttl)->fetch(); 305 | ``` 306 | 307 | **Notice:** `TTL` (Time To Live) is the same as `Laravel` cache. 308 | 309 | ```php 310 | // at the top 311 | use Shetabit\Extractor\Classes\Request; 312 | 313 | $url = 'http://google.com/'; 314 | $ttl = now()->addMinutes(10); // 10 minutes 315 | 316 | $response = (new Request)->setUri($url)->cache($ttl)->fetch(); 317 | ``` 318 | 319 | #### Conditional configs 320 | 321 | Sometimes you need to add some configs when a condition happens, in this kind of situations you can use the `when` method to add conditional configs. 322 | 323 | ```php 324 | # Example 1: simple 325 | 326 | $request 327 | ->when('condition1', function($request) { 328 | $request 329 | ->setUri('http://your-site.com') 330 | ->setMethod('get') 331 | ->middleware(new AuthMiddleware); 332 | }); 333 | 334 | 335 | // Example 2: nested 336 | $request 337 | ->when('condition1', function($request) { 338 | $request 339 | ->setUri('http://your-site.com') 340 | ->setMethod('get') 341 | ->middleware(new AuthMiddleware); 342 | }) 343 | ->when('condition2', function($request) { 344 | $request 345 | ->setUri('http://shop-site.com') 346 | ->setMethod('get'); 347 | }) 348 | ->whenNot('condition3', function($request) { 349 | $request 350 | ->setUri('http://shop-site.com') 351 | ->setMethod('patch') 352 | ->when('condition4', function($request) { 353 | $request->setMethod('delete'); // sets method to delete 354 | }); 355 | }) 356 | ->fetch(); 357 | ``` 358 | 359 | #### Client 360 | 361 | You can encapsulate any request that exists between the **current microservice** and the **remote microservice** within a `Client`. 362 | 363 | #### Create clients 364 | 365 | Clients can be created using a simple command 366 | 367 | ```bash 368 | php artisan make:extractor-client clientName 369 | ``` 370 | 371 | Clients will saved in `app/Http/RemoteRequests/Clients` by default. 372 | 373 | lets create and example, imagine you have and remote Api (or microservice) and need to login into it. 374 | 375 | then, your Login micro-client can be similar to below codes: 376 | 377 | ```php 378 | namespace App\Http\RemoteRequests\Clients\Auth; 379 | 380 | use Shetabit\Extractor\Abstracts\MicroClientAbstract; 381 | use Shetabit\Extractor\Contracts\ResponseInterface; 382 | 383 | class Login extends MicroClientAbstract 384 | { 385 | protected $mobile; 386 | protected $password; 387 | 388 | public function __construct($username, $password = null) 389 | { 390 | $this->username = $username; 391 | $this->password = $password; 392 | 393 | parent::__construct(); 394 | } 395 | 396 | /** 397 | * Get requests' endpoint 398 | * 399 | * @return string 400 | */ 401 | protected function getEndPoint() 402 | { 403 | return 'http://yoursite.com/api/v1/auth'; 404 | } 405 | 406 | /** 407 | * Run client 408 | * 409 | * @return ResponseInterface 410 | * @throws \Exception 411 | */ 412 | public function run() : ResponseInterface 413 | { 414 | $response = $this 415 | ->request 416 | ->setUri($this->getEndPoint()) 417 | ->setMethod('post') 418 | ->addFormParam('username', $this->username) 419 | ->addFormParam('password', $this->password) 420 | ->fetch(); 421 | 422 | return $response; 423 | } 424 | } 425 | ``` 426 | 427 | #### Run a client 428 | 429 | you can run the `Login` micro-client like the below (we have Login client example at the top) 430 | 431 | ```php 432 | // dump data 433 | $username = 'test'; 434 | $password = 'something'; 435 | 436 | $client = new Login($username, $password); 437 | 438 | // run client and login into remote service (remote api) 439 | $response = $client->run(); 440 | 441 | // dump show response's body 442 | var_dump($response->getBody()); 443 | ``` 444 | 445 | as you see, client starts to work as you call the `run` method, fetches and returns a response. 446 | 447 | ## On progress features 448 | 449 | - internal error exceptions 450 | - resource and API resource clients 451 | - proxy requests to another server (middleware) 452 | 453 | ## Change log 454 | 455 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 456 | 457 | ## Contributing 458 | 459 | Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details. 460 | 461 | ## Security 462 | 463 | If you discover any security related issues, please email khanzadimahdi@gmail.com instead of using the issue tracker. 464 | 465 | ## Credits 466 | 467 | - [Mahdi khanzadi][link-author] 468 | - [All Contributors][link-contributors] 469 | 470 | ## License 471 | 472 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 473 | 474 | [link-packagist]: https://packagist.org/packages/shetabit/extractor 475 | [link-code-quality]: https://scrutinizer-ci.com/g/shetabit/extractor 476 | [link-author]: https://github.com/khanzadimahdi 477 | [link-contributors]: ../../contributors 478 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shetabit/extractor", 3 | "type": "library", 4 | "description": "a `micro client` generator to communicate between `micro services` in laravel apps", 5 | "keywords": [ 6 | "shetabit", 7 | "extractor", 8 | "api extractor", 9 | "api client", 10 | "api remote client", 11 | "micro service", 12 | "micro service client", 13 | "micro service data extractor", 14 | "remote micro service" 15 | ], 16 | "homepage": "https://github.com/shetabit/extractor", 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "Mahdi Khanzadi", 21 | "email": "khanzadimahdi@gmail.com", 22 | "homepage": "https://github.com/shetabit", 23 | "role": "Developer" 24 | } 25 | ], 26 | "require": { 27 | "php": ">=7.2", 28 | "illuminate/broadcasting": "5.8.*|6.*|7.*|8.*|9.*|10.*", 29 | "illuminate/support": "5.8.*|6.*|7.*|8.*|9.*|10.*", 30 | "guzzlehttp/guzzle": "6.2.*|7.*" 31 | }, 32 | "require-dev": { 33 | "orchestra/testbench": ">=4.3 <5.0", 34 | "phpunit/phpunit": "^8.4", 35 | "squizlabs/php_codesniffer": "^3.5" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "Shetabit\\Extractor\\": "src" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "Shetabit\\Extractor\\Tests\\": "tests" 45 | } 46 | }, 47 | "scripts": { 48 | "test": "phpunit", 49 | "check-style": "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests", 50 | "fix-style": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests" 51 | }, 52 | "extra": { 53 | "laravel": { 54 | "providers": [ 55 | "Shetabit\\Extractor\\Providers\\ExtractorServiceProvider" 56 | ] 57 | } 58 | }, 59 | "config": { 60 | "sort-packages": true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./src/ 14 | 15 | 16 | 17 | 18 | ./tests/ 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /resources/images/microservices-communication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shetabit/extractor/53b76e39dd4e85075f2c497433818a89db78f576/resources/images/microservices-communication.png -------------------------------------------------------------------------------- /resources/images/middlewares-chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shetabit/extractor/53b76e39dd4e85075f2c497433818a89db78f576/resources/images/middlewares-chain.png -------------------------------------------------------------------------------- /src/Abstracts/MicroClientAbstract.php: -------------------------------------------------------------------------------- 1 | request = new Request; 25 | } 26 | 27 | /** 28 | * Run client 29 | * 30 | * @return ResponseInterface|null 31 | */ 32 | abstract public function run() : ?ResponseInterface; 33 | 34 | /** 35 | * Access to request methods directly 36 | * 37 | * @param $name 38 | * @param $params 39 | * 40 | * @return mixed 41 | */ 42 | public function __call($name, $params) 43 | { 44 | return call_user_func_array([$this->request, $name], $params); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Abstracts/MiddlewareAbstract.php: -------------------------------------------------------------------------------- 1 | createAndPrepareARequest($request); 42 | } 43 | 44 | $this->requests[] = [ 45 | 'request' => $requestInstance, 46 | 'response' => null, 47 | ]; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Retrieve all requests. 54 | * 55 | * @return mixed 56 | */ 57 | public function getRequests() 58 | { 59 | return $this->requests; 60 | } 61 | 62 | /** 63 | * Set max concurrency. 64 | * set it to 0 or negate values if you want max concurrency 65 | * 66 | * @param int $concurrency 67 | * @return $this 68 | */ 69 | public function setConcurrency(int $concurrency) 70 | { 71 | $this->concurrency = $concurrency; 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * Retrieve current concurrency. 78 | * 79 | * @return int 80 | */ 81 | public function getConcurrency() 82 | { 83 | return (int) $this->concurrency; 84 | } 85 | 86 | /** 87 | * Execute bag requests. 88 | * 89 | * @param callable|null $resolve 90 | * @param callable|null $reject 91 | * 92 | * @return array 93 | */ 94 | public function fetch(callable $resolve = null, callable $reject = null) : array 95 | { 96 | $client = new Client; 97 | $pool = new Pool($client, $this->prepareRequestPromises($client), $this->preparePoolConfigs($resolve, $reject)); 98 | 99 | // Initiate the transfers and create a promise 100 | $promise = $pool->promise(); 101 | 102 | // Force the pool of requests to complete. 103 | $promise->wait(); 104 | 105 | return array_column($this->getRequests(), 'response'); 106 | } 107 | 108 | /** 109 | * Prepare requests promise. 110 | * 111 | * @param $client 112 | * 113 | * @return \Generator 114 | */ 115 | protected function prepareRequestPromises($client) 116 | { 117 | foreach ($this->requests as $index => $data) { 118 | $request = $data['request']; 119 | 120 | yield function () use ($client, $request) { 121 | $promise = $client->requestAsync( 122 | $request->getMethod(), 123 | $request->getUri(), 124 | $request->getOptions() 125 | ); 126 | 127 | return $promise; 128 | }; 129 | } 130 | } 131 | 132 | /** 133 | * Create new request and set it configs by running given callback. 134 | * 135 | * @param null $callback 136 | * 137 | * @return Request 138 | */ 139 | protected function createAndPrepareARequest($callback = null) 140 | { 141 | $request = new BaseRequest(); 142 | 143 | if (is_callable($callback)) { 144 | $callback($request); 145 | } 146 | 147 | return $request; 148 | } 149 | 150 | /** 151 | * Prepare configs 152 | * 153 | * @param callable|null $resolve 154 | * @param callable|null $reject 155 | * 156 | * @return array 157 | */ 158 | protected function preparePoolConfigs(callable $resolve = null, callable $reject = null) 159 | { 160 | $concurrency = $this->getConcurrency() > 0 ? $this->getConcurrency() : count($this->requests); 161 | 162 | return [ 163 | 'concurrency' => $concurrency, 164 | 'fulfilled' => function ($result, $index) use ($resolve, $reject) { 165 | // this is delivered each successful response 166 | $request = $this->getRequests()[$index]['request']; 167 | 168 | $response = new Response( 169 | $request->getMethod(), 170 | $request->getUri(), 171 | $result->getHeaders(), 172 | $result->getBody(), 173 | $result->getStatusCode() 174 | ); 175 | 176 | $this->requests[$index]['response'] = $response; 177 | 178 | if ($response->getStatusCode() == 200) { // handle 200 OK response 179 | $request->success($response); 180 | if (is_callable($resolve)) { 181 | $resolve($response, $request); 182 | } 183 | } else { // handle responses has error status 184 | $request->error($response); 185 | if (is_callable($reject)) { 186 | $reject($response, $request); 187 | } 188 | } 189 | }, 190 | 'rejected' => function ($result, $index) use ($reject) { 191 | // this is delivered each failed request 192 | $request = $this->getRequests()[$index]['request']; 193 | $resolve = $this->getRequests()[$index]['resolve']; 194 | $reject = $this->getRequests()[$index]['reject']; 195 | $response = new Response( 196 | $request->getMethod(), 197 | $request->getUri(), 198 | [], 199 | '', 200 | 0 201 | ); 202 | 203 | $this->requests[$index]['response'] = $response; 204 | 205 | $request->error($response); 206 | if (is_callable($reject)) { 207 | $reject($response, $request); 208 | } 209 | }, 210 | ]; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/Classes/Request.php: -------------------------------------------------------------------------------- 1 | setUri($uri); 120 | $this->setMethod($method); 121 | $this->setBody($body); 122 | } 123 | 124 | /** 125 | * Set request's uri (endpoint) 126 | * 127 | * @param string $url 128 | * 129 | * @return $this|mixed 130 | */ 131 | public function setUri(string $url) 132 | { 133 | $this->uri = trim($url); 134 | 135 | $this->addUriQueries($this->uri); 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * Add uri's query string into query. 142 | * 143 | * @return $this|mixed 144 | */ 145 | protected function addUriQueries($uri) 146 | { 147 | $queries = $this->getParsedQueryString($uri); 148 | 149 | if (is_array($queries)) { 150 | foreach ($queries as $name => $value) { 151 | $this->addQuery($name, $value); 152 | } 153 | } 154 | 155 | return $this; 156 | } 157 | 158 | /** 159 | * Get request's endpoint 160 | * 161 | * @return string 162 | */ 163 | public function getUri() : string 164 | { 165 | return $this->uri; 166 | } 167 | 168 | /** 169 | * Get request's method (example: GET, POST, PUT, PATCH) 170 | * 171 | * @param string $method 172 | * 173 | * @return $this 174 | */ 175 | public function setMethod(string $method) 176 | { 177 | $this->method = $method; 178 | 179 | return $this; 180 | } 181 | 182 | /** 183 | * Get request's method 184 | * 185 | * @return string 186 | */ 187 | public function getMethod() : string 188 | { 189 | return $this->method; 190 | } 191 | 192 | /** 193 | * Add custom headers 194 | * 195 | * @param string $name 196 | * @param string $value 197 | * 198 | * @return $this 199 | */ 200 | public function addHeader(string $name, string $value) 201 | { 202 | $this->headers = array_merge($this->headers, [$name => $value]); 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Get header by its name 209 | * 210 | * @param string $name 211 | * 212 | * @return string 213 | */ 214 | public function getHeader(string $name) : string 215 | { 216 | return $this->headers[$name] ?? null; 217 | } 218 | 219 | /** 220 | * Retrieve all custom headers 221 | * 222 | * @return array 223 | */ 224 | public function getHeaders() : array 225 | { 226 | return $this->headers; 227 | } 228 | 229 | /** 230 | * Set request deadline (seconds) 231 | * 232 | * @param int $timeout 233 | * 234 | * @return $this 235 | */ 236 | public function setTimeout(int $timeout) 237 | { 238 | $this->timeout = $timeout; 239 | 240 | return $this; 241 | } 242 | 243 | /** 244 | * Get request's deadline (seconds) 245 | * 246 | * @return int 247 | */ 248 | public function getTimeout() : int 249 | { 250 | return (int) $this->timeout; 251 | } 252 | 253 | /** 254 | * Follow redirects or not 255 | * 256 | * @param bool $allow 257 | * 258 | * @return $this|mixed 259 | */ 260 | public function allowRedirects(bool $allow = true) 261 | { 262 | $this->allowRedirects = $allow; 263 | 264 | return $this; 265 | } 266 | 267 | /** 268 | * Set request's body 269 | * 270 | * @param $body 271 | * 272 | * @return $this|mixed 273 | */ 274 | public function setBody($body) 275 | { 276 | $this->body = $body; 277 | 278 | return $this; 279 | } 280 | 281 | /** 282 | * Get request's body 283 | * 284 | * @return string|null 285 | */ 286 | public function getBody() : ?string 287 | { 288 | return $this->body; 289 | } 290 | 291 | /** 292 | * Add form data 293 | * 294 | * @param $name 295 | * @param $value 296 | * 297 | * @return $this 298 | */ 299 | public function addFormParam($name, $value) 300 | { 301 | $this->formParams = array_merge($this->formParams, [$name => $value]); 302 | 303 | return $this; 304 | } 305 | 306 | /** 307 | * Retrieve multipart data 308 | * if name is empty , all data will returned 309 | * 310 | * @param $name 311 | * 312 | * @return mixed|null 313 | */ 314 | public function getFormParam($name) 315 | { 316 | return $this->formParams[$name] ?? null; 317 | } 318 | 319 | /** 320 | * Retrieve all form params 321 | * 322 | * @return array 323 | */ 324 | public function getFormParams() 325 | { 326 | return $this->formParams; 327 | } 328 | 329 | /** 330 | * Add form data 331 | * 332 | * @param $name 333 | * @param $value 334 | * @param array $headers 335 | * 336 | * @return $this 337 | */ 338 | public function addMultiparData($name, $value, array $headers = []) 339 | { 340 | $data = [ 341 | 'name' => $name, 342 | 'contents' => $value, 343 | 'headers' => $headers 344 | ]; 345 | 346 | array_push($this->multipartData, $data); 347 | 348 | return $this; 349 | } 350 | 351 | 352 | /** 353 | * Retrieve multipart data 354 | * if name is empty , all data will returned 355 | * 356 | * @param null $name 357 | * 358 | * @return array|mixed|null 359 | */ 360 | public function getMultipartData($name = null) 361 | { 362 | return empty($name) ? $this->multipartData : ($this->multipartData[$name] ?? null); 363 | } 364 | 365 | /** 366 | * Add query 367 | * 368 | * @param $name 369 | * @param $value 370 | * 371 | * @return $this 372 | */ 373 | public function addQuery($name, $value) 374 | { 375 | $this->queries = array_merge($this->queries, [$name => $value]); 376 | 377 | return $this; 378 | } 379 | 380 | /** 381 | * Retrieve multipart data 382 | * if name is empty , all data will returned 383 | * 384 | * @param $name 385 | * 386 | * @return mixed|null 387 | */ 388 | public function getQuery($name) 389 | { 390 | return $this->queries[$name] ?? null; 391 | } 392 | 393 | /** 394 | * Get request's queries 395 | * 396 | * @return array 397 | */ 398 | public function getQueries() 399 | { 400 | return $this->queries; 401 | } 402 | 403 | /** 404 | * Set a proxy 405 | * 406 | * @param mixed $proxy 407 | * 408 | * @return $this 409 | */ 410 | public function setProxy($proxy) 411 | { 412 | $this->proxy = $proxy; 413 | 414 | return $this; 415 | } 416 | 417 | /** 418 | * Retrieve the current proxy 419 | * 420 | * @return mixed|null 421 | */ 422 | public function getProxy() 423 | { 424 | return $this->proxy; 425 | } 426 | 427 | /** 428 | * Describes the SSL certificate verification behavior of a request. 429 | * 430 | * @param mixed $verify 431 | * 432 | * @return $this 433 | */ 434 | public function setVerify($verify) 435 | { 436 | $this->verify = $verify; 437 | 438 | return $this; 439 | } 440 | 441 | /** 442 | * Retrieve SSL certificate verification behavior. 443 | * 444 | * @return bool|string 445 | */ 446 | public function getVerify() 447 | { 448 | return $this->verify; 449 | } 450 | 451 | /** 452 | * Generate options 453 | * 454 | * @return array 455 | */ 456 | public function getOptions() 457 | { 458 | $options = [ 459 | 'http_errors' => false, 460 | 'body' => $this->getBody(), 461 | 'query' => $this->getQueries(), 462 | 'headers' => $this->getHeaders(), 463 | 'verify' => $this->getVerify(), 464 | ]; 465 | 466 | if ($proxy = $this->getProxy()) { 467 | $options['proxy'] = $proxy; 468 | } 469 | 470 | /* 471 | * we can't use formParams and MultipartData at the same time. 472 | * this part selects one of them. 473 | */ 474 | if (!empty($this->getFormParams())) { 475 | $options['form_params'] = $this->getFormParams(); 476 | } elseif (!empty($this->getMultipartData())) { 477 | $options['multipart'] = $this->getMultipartData(); 478 | } 479 | 480 | return $options; 481 | } 482 | 483 | /** 484 | * This event will be invoked when fetch complete successfully. 485 | * 486 | * @param callable $callback 487 | * 488 | * @return $this 489 | */ 490 | public function onSuccess(callable $callback) 491 | { 492 | $this->onSuccessCallback = $callback; 493 | 494 | return $this; 495 | } 496 | 497 | /** 498 | * Trigger success event 499 | * 500 | * @param ResponseInterface $response 501 | * 502 | * @return $this 503 | */ 504 | public function success(ResponseInterface $response) 505 | { 506 | if (is_callable($this->onSuccessCallback)) { 507 | $success = $this->onSuccessCallback; 508 | $success($response, $this); 509 | } 510 | 511 | return $this; 512 | } 513 | 514 | /** 515 | * This event will be invoked when fetch fail. 516 | * 517 | * @param callable $callback 518 | * 519 | * @return $this 520 | */ 521 | public function onError(callable $callback) 522 | { 523 | $this->onErrorCallback = $callback; 524 | 525 | return $this; 526 | } 527 | 528 | /** 529 | * Trigger error event 530 | * 531 | * @param ResponseInterface $response 532 | * 533 | * @return $this 534 | */ 535 | public function error(ResponseInterface $response) 536 | { 537 | if (is_callable($this->onErrorCallback)) { 538 | $error = $this->onErrorCallback; 539 | $error($response, $this); 540 | } 541 | 542 | return $this; 543 | } 544 | 545 | /** 546 | * Run and fetch data 547 | * 548 | * @param callable|null $resolve 549 | * @param callable|null $reject 550 | * 551 | * @return ResponseInterface 552 | * 553 | * @throws \GuzzleHttp\Exception\GuzzleException 554 | */ 555 | public function fetch(callable $resolve = null, callable $reject = null) : ResponseInterface 556 | { 557 | if (is_callable($resolve)) { 558 | $this->onSuccess($resolve); 559 | } 560 | 561 | if (is_callable($reject)) { 562 | $this->onError($reject); 563 | } 564 | 565 | $next = function($request) { 566 | $client = new Client([ 567 | // Base URI is used with relative requests 568 | 'base_uri' => $request->uri, 569 | 570 | // You can set any number of default request options. 571 | 'timeout' => $request->getTimeout(), 572 | ]); 573 | 574 | $result = $client->request($request->getMethod(), $request->getUri(), $request->getOptions()); 575 | 576 | $response = new Response( 577 | $request->getMethod(), 578 | $request->getUri(), 579 | $result->getHeaders(), 580 | $result->getBody(), 581 | $result->getStatusCode() 582 | ); 583 | 584 | return $response; 585 | }; 586 | 587 | $response = $this->invokeMiddlewares($this, $next); 588 | 589 | if ($response->getStatusCode() == 200) { // handle 200 OK response 590 | if (is_callable($this->onSuccessCallback)) { 591 | $this->success($response); 592 | } 593 | } else { 594 | if (is_callable($this->onErrorCallback)) { // handle responses has error status 595 | $this->error($response); 596 | } 597 | } 598 | 599 | return $response; 600 | } 601 | 602 | /** 603 | * An alias for fetch 604 | * 605 | * @param callable|null $resolve 606 | * @param callable|null $reject 607 | * 608 | * @return ResponseInterface 609 | * 610 | * @throws \GuzzleHttp\Exception\GuzzleException 611 | */ 612 | public function send(callable $resolve = null, callable $reject = null) 613 | { 614 | return $this->fetch($resolve, $reject); 615 | } 616 | 617 | /** 618 | * Create concurrent requests (factory method). 619 | * 620 | * @return Bag 621 | */ 622 | public function createBag() 623 | { 624 | return new Bag(); 625 | } 626 | } 627 | -------------------------------------------------------------------------------- /src/Classes/Response.php: -------------------------------------------------------------------------------- 1 | method= $method; 59 | $this->uri = $uri; 60 | $this->headers = $headers; 61 | $this->body = $body; 62 | $this->statusCode = $statusCode; 63 | } 64 | 65 | /** 66 | * Get response's uri 67 | * 68 | * @return string 69 | */ 70 | public function getUri() : string 71 | { 72 | return $this->uri; 73 | } 74 | 75 | /** 76 | * Get response's method 77 | * 78 | * @return string 79 | */ 80 | public function getMethod() : string 81 | { 82 | return $this->method; 83 | } 84 | 85 | /** 86 | * Get response's header by its name 87 | * 88 | * @param string $name 89 | * @return mixed|string|null 90 | */ 91 | public function getHeader(string $name) 92 | { 93 | return $this->headers[$name] ?? null; 94 | } 95 | 96 | /** 97 | * Get all response's header 98 | * 99 | * @return array 100 | */ 101 | public function getHeaders() : array 102 | { 103 | return $this->headers; 104 | } 105 | 106 | /** 107 | * Get response's body 108 | * 109 | * @return string 110 | */ 111 | public function getBody() : string 112 | { 113 | return $this->body; 114 | } 115 | 116 | /** 117 | * get response's status code 118 | * 119 | * @return int 120 | */ 121 | public function getStatusCode() : int 122 | { 123 | return $this->statusCode; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Console/Commands/MicroClientMakeCommand.php: -------------------------------------------------------------------------------- 1 | request->setUri('remote-url.com')->fetch(); // returns ResponseInterface 29 | * 30 | * return $response; 31 | */ 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Console/stubs/middleware.stub: -------------------------------------------------------------------------------- 1 | ttl = $ttl; 25 | } 26 | 27 | /** 28 | * Handle request and return suitable response 29 | * 30 | * @param RequestInterface $request 31 | * @param Closure $next 32 | * 33 | * @return ResponseInterface 34 | */ 35 | public function handle(RequestInterface $request, Closure $next) : ?ResponseInterface 36 | { 37 | $key = $this->getCacheKey($request); 38 | 39 | if ($this->cacheExists($key)) { 40 | return $this->retrieveFromCache($key); 41 | } 42 | 43 | return $this->storeInCache($key, $next($request)); 44 | } 45 | 46 | /** 47 | * Determine if cache exists 48 | * 49 | * @param string $key 50 | * 51 | * @return bool 52 | */ 53 | protected function cacheExists(string $key) : bool 54 | { 55 | return Cache::has($key); 56 | } 57 | 58 | /** 59 | * Store response in cache 60 | * 61 | * @param string $key 62 | * @param ResponseInterface $response 63 | * 64 | * @return ResponseInterface 65 | */ 66 | protected function storeInCache(string $key, ResponseInterface $response) : ?ResponseInterface 67 | { 68 | return Cache::remember($key, $this->ttl, $response); 69 | } 70 | 71 | /** 72 | * Retrieve response from cache 73 | * 74 | * @param string $key 75 | * 76 | * @return ResponseInterface 77 | */ 78 | protected function retrieveFromCache(string $key) : ?ResponseInterface 79 | { 80 | return Cache::get($key); 81 | } 82 | 83 | /** 84 | * Create a unique key for given request 85 | * 86 | * @param RequestInterface $request 87 | * 88 | * @return string 89 | */ 90 | protected function getCacheKey(RequestInterface $request) : string 91 | { 92 | return sha1(serialize($request)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Middlewares/CacheMiddleware.php: -------------------------------------------------------------------------------- 1 | ttl = $ttl; 26 | } 27 | 28 | /** 29 | * Handle request and return suitable response 30 | * 31 | * @param RequestInterface $request 32 | * @param Closure $next 33 | * 34 | * @return ResponseInterface 35 | */ 36 | public function handle(RequestInterface $request, Closure $next) : ?ResponseInterface 37 | { 38 | $key = $this->getCacheKey($request); 39 | 40 | if ($this->cacheExists($key)) { 41 | return $this->retrieveFromCache($key); 42 | } 43 | 44 | return $this->storeInCache($key, $next($request)); 45 | } 46 | 47 | /** 48 | * Determine if cache exists 49 | * 50 | * @param string $key 51 | * 52 | * @return bool 53 | */ 54 | protected function cacheExists(string $key) : bool 55 | { 56 | return Cache::has($key); 57 | } 58 | 59 | /** 60 | * Store response in cache 61 | * 62 | * @param string $key 63 | * @param ResponseInterface $response 64 | * 65 | * @return ResponseInterface 66 | */ 67 | protected function storeInCache(string $key, ResponseInterface $response) : ?ResponseInterface 68 | { 69 | Cache::put($key, $response, $this->ttl); 70 | 71 | return $response; 72 | } 73 | 74 | /** 75 | * Retrieve response from cache 76 | * 77 | * @param string $key 78 | * 79 | * @return ResponseInterface 80 | */ 81 | protected function retrieveFromCache(string $key) : ?ResponseInterface 82 | { 83 | return Cache::get($key); 84 | } 85 | 86 | /** 87 | * Create a unique key for given request 88 | * 89 | * @param RequestInterface $request 90 | * 91 | * @return string 92 | */ 93 | protected function getCacheKey(RequestInterface $request) : string 94 | { 95 | return sha1(serialize($request)); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Middlewares/Middleware.php: -------------------------------------------------------------------------------- 1 | loadCommands(); 20 | } 21 | 22 | /** 23 | * Register any package services. 24 | * 25 | * @return void 26 | */ 27 | public function register() 28 | { 29 | // 30 | } 31 | 32 | /** 33 | * Load artisan commands 34 | * 35 | * @return void 36 | */ 37 | protected function loadCommands() 38 | { 39 | if ($this->app->runningInConsole()) { 40 | $this->commands([ 41 | MicroClientMakeCommand::class, 42 | MicroClientMiddlewareMakeCommand::class, 43 | ]); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Traits/Conditional.php: -------------------------------------------------------------------------------- 1 | bannedMiddlewares, $middleware); 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * Bind cache middleware 67 | * 68 | * @param $ttl 69 | * 70 | * @return $this 71 | */ 72 | public function cache($ttl = 10) 73 | { 74 | $this->middleware(new CacheMiddleware($ttl)); 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * Add middlewares 81 | * 82 | * @param MiddlewareInterface $middleware 83 | * 84 | * @return $this 85 | */ 86 | public function middleware(MiddlewareInterface $middleware) 87 | { 88 | array_push($this->middlewares, $middleware); 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * Retrieve a chain of middlewares 95 | * 96 | * @return null|ResponseInterface 97 | */ 98 | protected function invokeMiddlewares($request, $callback) : ?ResponseInterface 99 | { 100 | $middlewares = array_diff( 101 | array_merge(static::$globalMiddlewares, $this->middlewares), 102 | $this->bannedMiddlewares 103 | ); 104 | 105 | $middlewares = array_reverse($middlewares); 106 | 107 | $next = function ($request) use ($callback) { 108 | return $callback($request); 109 | }; 110 | 111 | foreach ($middlewares as $middleware) { 112 | $next = function ($request) use ($middleware, $next) { 113 | return $middleware->handle($request, $next); 114 | }; 115 | } 116 | 117 | return $next($request); 118 | } 119 | } -------------------------------------------------------------------------------- /src/Traits/HasParsedUri.php: -------------------------------------------------------------------------------- 1 | parseURL($this->getUri()); 17 | } 18 | 19 | /** 20 | * Parse query string 21 | * 22 | * @return array 23 | */ 24 | public function getParsedQueryString() 25 | { 26 | $parsedUri = $this->getParsedUri(); 27 | 28 | $queryString = $parsedUri['query'] ?? null; 29 | 30 | return $this->parseQueryString($queryString); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Traits/HttpURL.php: -------------------------------------------------------------------------------- 1 | 'Shetabit\Extractor\Facade\Extractor', 18 | ]; 19 | } 20 | } 21 | --------------------------------------------------------------------------------