├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── example.php ├── phpunit.xml.dist ├── src └── Igorw │ └── Stack │ ├── OAuth.php │ ├── OAuth │ ├── AuthController.php │ └── ContainerConfig.php │ └── Router.php └── tests └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Igor Wiedler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stack/OAuth 2 | 3 | OAuth stack middleware. 4 | 5 | ## Requirements 6 | 7 | * **session**: The request must have session handling accounted for. You can 8 | do this by prepending the `stack/session` middleware to this one. 9 | 10 | * **credentials**: You need to have some sort of OAuth server. By default, 11 | `stack/oauth` will use twitter. But you can change that through the 12 | `oauth_service.class` config parameter. 13 | 14 | ## Usage 15 | 16 | use Igorw\Stack\OAuth; 17 | 18 | $app = new OAuth($app, [ 19 | 'key' => 'foo', 20 | 'secret' => 'bar', 21 | 'callback_url' => 'http://localhost:8080/auth/verify', 22 | 'success_url' => '/', 23 | 'failure_url' => '/auth', 24 | ]); 25 | 26 | ## Pre-defined URLs 27 | 28 | * /auth 29 | * /auth/verify 30 | 31 | ## TODO 32 | 33 | * config validation 34 | * tests 35 | * more flexible path config (?) 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "igorw/stack-oauth", 3 | "description": "OAuth stack middleware.", 4 | "keywords": ["stack", "oauth"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Igor Wiedler", 9 | "email": "igor@wiedler.ch" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.4.0", 14 | "lusitanian/oauth": "0.1.*", 15 | "pimple/pimple": "~1.0@dev", 16 | "symfony/http-foundation": "~2.1", 17 | "symfony/http-kernel": "~2.1" 18 | }, 19 | "require-dev": { 20 | "silex/silex": "~1.0@dev", 21 | "stack/builder": "~1.0@dev", 22 | "stack/session": "~1.0@dev", 23 | "stack/callable-http-kernel": "~1.0@dev", 24 | "phpunit/phpunit": "3.7.*" 25 | }, 26 | "suggest": { 27 | "stack/session": "~1.0@dev" 28 | }, 29 | "autoload": { 30 | "psr-0": { "Igorw\\Stack": "src" } 31 | }, 32 | "extra": { 33 | "branch-alias": { "dev-master": "1.0-dev" } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" 5 | ], 6 | "hash": "5d40353bdc8dbd1d18b1509e50d909c6", 7 | "packages": [ 8 | { 9 | "name": "lusitanian/oauth", 10 | "version": "v0.1.4", 11 | "source": { 12 | "type": "git", 13 | "url": "https://github.com/Lusitanian/PHPoAuthLib.git", 14 | "reference": "b0508008fca802a5970818b18550e01c87f02898" 15 | }, 16 | "dist": { 17 | "type": "zip", 18 | "url": "https://api.github.com/repos/Lusitanian/PHPoAuthLib/zipball/b0508008fca802a5970818b18550e01c87f02898", 19 | "reference": "b0508008fca802a5970818b18550e01c87f02898", 20 | "shasum": "" 21 | }, 22 | "require": { 23 | "php": ">=5.4.0" 24 | }, 25 | "require-dev": { 26 | "symfony/http-foundation": "~2.1" 27 | }, 28 | "type": "library", 29 | "extra": { 30 | "branch-alias": { 31 | "dev-master": "0.1-dev" 32 | } 33 | }, 34 | "autoload": { 35 | "psr-0": { 36 | "OAuth": "src" 37 | } 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "David Desberg", 46 | "email": "david@daviddesberg.com" 47 | }, 48 | { 49 | "name": "Pieter Hordijk", 50 | "email": "info@pieterhordijk.com", 51 | "homepage": "https://pieterhordijk.com", 52 | "role": "developer" 53 | } 54 | ], 55 | "description": "PHP 5.4+ oAuth 1/2 Library", 56 | "keywords": [ 57 | "Authentication", 58 | "authorization", 59 | "oauth", 60 | "security" 61 | ], 62 | "time": "2013-02-27 18:44:02" 63 | }, 64 | { 65 | "name": "pimple/pimple", 66 | "version": "1.1.x-dev", 67 | "source": { 68 | "type": "git", 69 | "url": "https://github.com/fabpot/Pimple.git", 70 | "reference": "471c7d7c52ad6594e17b8ec33efdd1be592b5d83" 71 | }, 72 | "dist": { 73 | "type": "zip", 74 | "url": "https://api.github.com/repos/fabpot/Pimple/zipball/471c7d7c52ad6594e17b8ec33efdd1be592b5d83", 75 | "reference": "471c7d7c52ad6594e17b8ec33efdd1be592b5d83", 76 | "shasum": "" 77 | }, 78 | "require": { 79 | "php": ">=5.3.0" 80 | }, 81 | "type": "library", 82 | "extra": { 83 | "branch-alias": { 84 | "dev-master": "1.1.x-dev" 85 | } 86 | }, 87 | "autoload": { 88 | "psr-0": { 89 | "Pimple": "lib/" 90 | } 91 | }, 92 | "notification-url": "https://packagist.org/downloads/", 93 | "license": [ 94 | "MIT" 95 | ], 96 | "authors": [ 97 | { 98 | "name": "Fabien Potencier", 99 | "email": "fabien@symfony.com" 100 | } 101 | ], 102 | "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", 103 | "homepage": "http://pimple.sensiolabs.org", 104 | "keywords": [ 105 | "container", 106 | "dependency injection" 107 | ], 108 | "time": "2013-09-19 04:53:08" 109 | }, 110 | { 111 | "name": "psr/log", 112 | "version": "1.0.0", 113 | "source": { 114 | "type": "git", 115 | "url": "https://github.com/php-fig/log.git", 116 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 117 | }, 118 | "dist": { 119 | "type": "zip", 120 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 121 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 122 | "shasum": "" 123 | }, 124 | "type": "library", 125 | "autoload": { 126 | "psr-0": { 127 | "Psr\\Log\\": "" 128 | } 129 | }, 130 | "notification-url": "https://packagist.org/downloads/", 131 | "license": [ 132 | "MIT" 133 | ], 134 | "authors": [ 135 | { 136 | "name": "PHP-FIG", 137 | "homepage": "http://www.php-fig.org/" 138 | } 139 | ], 140 | "description": "Common interface for logging libraries", 141 | "keywords": [ 142 | "log", 143 | "psr", 144 | "psr-3" 145 | ], 146 | "time": "2012-12-21 11:40:51" 147 | }, 148 | { 149 | "name": "symfony/debug", 150 | "version": "v2.3.6", 151 | "target-dir": "Symfony/Component/Debug", 152 | "source": { 153 | "type": "git", 154 | "url": "https://github.com/symfony/Debug.git", 155 | "reference": "7f671456b9617d0a54f04597ae1c1ed3a1e858fd" 156 | }, 157 | "dist": { 158 | "type": "zip", 159 | "url": "https://api.github.com/repos/symfony/Debug/zipball/7f671456b9617d0a54f04597ae1c1ed3a1e858fd", 160 | "reference": "7f671456b9617d0a54f04597ae1c1ed3a1e858fd", 161 | "shasum": "" 162 | }, 163 | "require": { 164 | "php": ">=5.3.3" 165 | }, 166 | "require-dev": { 167 | "symfony/http-foundation": "~2.1", 168 | "symfony/http-kernel": "~2.1" 169 | }, 170 | "suggest": { 171 | "symfony/class-loader": "", 172 | "symfony/http-foundation": "", 173 | "symfony/http-kernel": "" 174 | }, 175 | "type": "library", 176 | "extra": { 177 | "branch-alias": { 178 | "dev-master": "2.3-dev" 179 | } 180 | }, 181 | "autoload": { 182 | "psr-0": { 183 | "Symfony\\Component\\Debug\\": "" 184 | } 185 | }, 186 | "notification-url": "https://packagist.org/downloads/", 187 | "license": [ 188 | "MIT" 189 | ], 190 | "authors": [ 191 | { 192 | "name": "Fabien Potencier", 193 | "email": "fabien@symfony.com" 194 | }, 195 | { 196 | "name": "Symfony Community", 197 | "homepage": "http://symfony.com/contributors" 198 | } 199 | ], 200 | "description": "Symfony Debug Component", 201 | "homepage": "http://symfony.com", 202 | "time": "2013-09-19 09:47:13" 203 | }, 204 | { 205 | "name": "symfony/event-dispatcher", 206 | "version": "v2.3.6", 207 | "target-dir": "Symfony/Component/EventDispatcher", 208 | "source": { 209 | "type": "git", 210 | "url": "https://github.com/symfony/EventDispatcher.git", 211 | "reference": "7fc72a7a346a1887d3968cc1ce5642a15cd182e9" 212 | }, 213 | "dist": { 214 | "type": "zip", 215 | "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/7fc72a7a346a1887d3968cc1ce5642a15cd182e9", 216 | "reference": "7fc72a7a346a1887d3968cc1ce5642a15cd182e9", 217 | "shasum": "" 218 | }, 219 | "require": { 220 | "php": ">=5.3.3" 221 | }, 222 | "require-dev": { 223 | "symfony/dependency-injection": "~2.0" 224 | }, 225 | "suggest": { 226 | "symfony/dependency-injection": "", 227 | "symfony/http-kernel": "" 228 | }, 229 | "type": "library", 230 | "extra": { 231 | "branch-alias": { 232 | "dev-master": "2.3-dev" 233 | } 234 | }, 235 | "autoload": { 236 | "psr-0": { 237 | "Symfony\\Component\\EventDispatcher\\": "" 238 | } 239 | }, 240 | "notification-url": "https://packagist.org/downloads/", 241 | "license": [ 242 | "MIT" 243 | ], 244 | "authors": [ 245 | { 246 | "name": "Fabien Potencier", 247 | "email": "fabien@symfony.com" 248 | }, 249 | { 250 | "name": "Symfony Community", 251 | "homepage": "http://symfony.com/contributors" 252 | } 253 | ], 254 | "description": "Symfony EventDispatcher Component", 255 | "homepage": "http://symfony.com", 256 | "time": "2013-09-19 09:45:20" 257 | }, 258 | { 259 | "name": "symfony/http-foundation", 260 | "version": "v2.3.6", 261 | "target-dir": "Symfony/Component/HttpFoundation", 262 | "source": { 263 | "type": "git", 264 | "url": "https://github.com/symfony/HttpFoundation.git", 265 | "reference": "59e712338cd05463ebcb8da6422a01b1291871e3" 266 | }, 267 | "dist": { 268 | "type": "zip", 269 | "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/59e712338cd05463ebcb8da6422a01b1291871e3", 270 | "reference": "59e712338cd05463ebcb8da6422a01b1291871e3", 271 | "shasum": "" 272 | }, 273 | "require": { 274 | "php": ">=5.3.3" 275 | }, 276 | "type": "library", 277 | "extra": { 278 | "branch-alias": { 279 | "dev-master": "2.3-dev" 280 | } 281 | }, 282 | "autoload": { 283 | "psr-0": { 284 | "Symfony\\Component\\HttpFoundation\\": "" 285 | }, 286 | "classmap": [ 287 | "Symfony/Component/HttpFoundation/Resources/stubs" 288 | ] 289 | }, 290 | "notification-url": "https://packagist.org/downloads/", 291 | "license": [ 292 | "MIT" 293 | ], 294 | "authors": [ 295 | { 296 | "name": "Fabien Potencier", 297 | "email": "fabien@symfony.com" 298 | }, 299 | { 300 | "name": "Symfony Community", 301 | "homepage": "http://symfony.com/contributors" 302 | } 303 | ], 304 | "description": "Symfony HttpFoundation Component", 305 | "homepage": "http://symfony.com", 306 | "time": "2013-09-29 19:41:41" 307 | }, 308 | { 309 | "name": "symfony/http-kernel", 310 | "version": "v2.3.6", 311 | "target-dir": "Symfony/Component/HttpKernel", 312 | "source": { 313 | "type": "git", 314 | "url": "https://github.com/symfony/HttpKernel.git", 315 | "reference": "9ace7b963fa6d2eae8471c246e6fabc2ef714566" 316 | }, 317 | "dist": { 318 | "type": "zip", 319 | "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/9ace7b963fa6d2eae8471c246e6fabc2ef714566", 320 | "reference": "9ace7b963fa6d2eae8471c246e6fabc2ef714566", 321 | "shasum": "" 322 | }, 323 | "require": { 324 | "php": ">=5.3.3", 325 | "psr/log": "~1.0", 326 | "symfony/debug": "~2.3", 327 | "symfony/event-dispatcher": "~2.1", 328 | "symfony/http-foundation": "~2.2" 329 | }, 330 | "require-dev": { 331 | "symfony/browser-kit": "~2.2", 332 | "symfony/class-loader": "~2.1", 333 | "symfony/config": "~2.0", 334 | "symfony/console": "~2.2", 335 | "symfony/dependency-injection": "~2.0", 336 | "symfony/finder": "~2.0", 337 | "symfony/process": "~2.0", 338 | "symfony/routing": "~2.2", 339 | "symfony/stopwatch": "~2.2", 340 | "symfony/templating": "~2.2" 341 | }, 342 | "suggest": { 343 | "symfony/browser-kit": "", 344 | "symfony/class-loader": "", 345 | "symfony/config": "", 346 | "symfony/console": "", 347 | "symfony/dependency-injection": "", 348 | "symfony/finder": "" 349 | }, 350 | "type": "library", 351 | "extra": { 352 | "branch-alias": { 353 | "dev-master": "2.3-dev" 354 | } 355 | }, 356 | "autoload": { 357 | "psr-0": { 358 | "Symfony\\Component\\HttpKernel\\": "" 359 | } 360 | }, 361 | "notification-url": "https://packagist.org/downloads/", 362 | "license": [ 363 | "MIT" 364 | ], 365 | "authors": [ 366 | { 367 | "name": "Fabien Potencier", 368 | "email": "fabien@symfony.com" 369 | }, 370 | { 371 | "name": "Symfony Community", 372 | "homepage": "http://symfony.com/contributors" 373 | } 374 | ], 375 | "description": "Symfony HttpKernel Component", 376 | "homepage": "http://symfony.com", 377 | "time": "2013-10-10 13:24:22" 378 | } 379 | ], 380 | "packages-dev": [ 381 | { 382 | "name": "phpunit/php-code-coverage", 383 | "version": "1.2.13", 384 | "source": { 385 | "type": "git", 386 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 387 | "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94" 388 | }, 389 | "dist": { 390 | "type": "zip", 391 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", 392 | "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", 393 | "shasum": "" 394 | }, 395 | "require": { 396 | "php": ">=5.3.3", 397 | "phpunit/php-file-iterator": ">=1.3.0@stable", 398 | "phpunit/php-text-template": ">=1.1.1@stable", 399 | "phpunit/php-token-stream": ">=1.1.3@stable" 400 | }, 401 | "require-dev": { 402 | "phpunit/phpunit": "3.7.*@dev" 403 | }, 404 | "suggest": { 405 | "ext-dom": "*", 406 | "ext-xdebug": ">=2.0.5" 407 | }, 408 | "type": "library", 409 | "extra": { 410 | "branch-alias": { 411 | "dev-master": "1.2.x-dev" 412 | } 413 | }, 414 | "autoload": { 415 | "classmap": [ 416 | "PHP/" 417 | ] 418 | }, 419 | "notification-url": "https://packagist.org/downloads/", 420 | "include-path": [ 421 | "" 422 | ], 423 | "license": [ 424 | "BSD-3-Clause" 425 | ], 426 | "authors": [ 427 | { 428 | "name": "Sebastian Bergmann", 429 | "email": "sb@sebastian-bergmann.de", 430 | "role": "lead" 431 | } 432 | ], 433 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 434 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 435 | "keywords": [ 436 | "coverage", 437 | "testing", 438 | "xunit" 439 | ], 440 | "time": "2013-09-10 08:14:32" 441 | }, 442 | { 443 | "name": "phpunit/php-file-iterator", 444 | "version": "1.3.4", 445 | "source": { 446 | "type": "git", 447 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 448 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" 449 | }, 450 | "dist": { 451 | "type": "zip", 452 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", 453 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", 454 | "shasum": "" 455 | }, 456 | "require": { 457 | "php": ">=5.3.3" 458 | }, 459 | "type": "library", 460 | "autoload": { 461 | "classmap": [ 462 | "File/" 463 | ] 464 | }, 465 | "notification-url": "https://packagist.org/downloads/", 466 | "include-path": [ 467 | "" 468 | ], 469 | "license": [ 470 | "BSD-3-Clause" 471 | ], 472 | "authors": [ 473 | { 474 | "name": "Sebastian Bergmann", 475 | "email": "sb@sebastian-bergmann.de", 476 | "role": "lead" 477 | } 478 | ], 479 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 480 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 481 | "keywords": [ 482 | "filesystem", 483 | "iterator" 484 | ], 485 | "time": "2013-10-10 15:34:57" 486 | }, 487 | { 488 | "name": "phpunit/php-text-template", 489 | "version": "1.1.4", 490 | "source": { 491 | "type": "git", 492 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 493 | "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23" 494 | }, 495 | "dist": { 496 | "type": "zip", 497 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5180896f51c5b3648ac946b05f9ec02be78a0b23", 498 | "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23", 499 | "shasum": "" 500 | }, 501 | "require": { 502 | "php": ">=5.3.3" 503 | }, 504 | "type": "library", 505 | "autoload": { 506 | "classmap": [ 507 | "Text/" 508 | ] 509 | }, 510 | "notification-url": "https://packagist.org/downloads/", 511 | "include-path": [ 512 | "" 513 | ], 514 | "license": [ 515 | "BSD-3-Clause" 516 | ], 517 | "authors": [ 518 | { 519 | "name": "Sebastian Bergmann", 520 | "email": "sb@sebastian-bergmann.de", 521 | "role": "lead" 522 | } 523 | ], 524 | "description": "Simple template engine.", 525 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 526 | "keywords": [ 527 | "template" 528 | ], 529 | "time": "2012-10-31 18:15:28" 530 | }, 531 | { 532 | "name": "phpunit/php-timer", 533 | "version": "1.0.5", 534 | "source": { 535 | "type": "git", 536 | "url": "https://github.com/sebastianbergmann/php-timer.git", 537 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" 538 | }, 539 | "dist": { 540 | "type": "zip", 541 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 542 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 543 | "shasum": "" 544 | }, 545 | "require": { 546 | "php": ">=5.3.3" 547 | }, 548 | "type": "library", 549 | "autoload": { 550 | "classmap": [ 551 | "PHP/" 552 | ] 553 | }, 554 | "notification-url": "https://packagist.org/downloads/", 555 | "include-path": [ 556 | "" 557 | ], 558 | "license": [ 559 | "BSD-3-Clause" 560 | ], 561 | "authors": [ 562 | { 563 | "name": "Sebastian Bergmann", 564 | "email": "sb@sebastian-bergmann.de", 565 | "role": "lead" 566 | } 567 | ], 568 | "description": "Utility class for timing", 569 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 570 | "keywords": [ 571 | "timer" 572 | ], 573 | "time": "2013-08-02 07:42:54" 574 | }, 575 | { 576 | "name": "phpunit/php-token-stream", 577 | "version": "1.2.1", 578 | "source": { 579 | "type": "git", 580 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 581 | "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e" 582 | }, 583 | "dist": { 584 | "type": "zip", 585 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", 586 | "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", 587 | "shasum": "" 588 | }, 589 | "require": { 590 | "ext-tokenizer": "*", 591 | "php": ">=5.3.3" 592 | }, 593 | "type": "library", 594 | "extra": { 595 | "branch-alias": { 596 | "dev-master": "1.2-dev" 597 | } 598 | }, 599 | "autoload": { 600 | "classmap": [ 601 | "PHP/" 602 | ] 603 | }, 604 | "notification-url": "https://packagist.org/downloads/", 605 | "include-path": [ 606 | "" 607 | ], 608 | "license": [ 609 | "BSD-3-Clause" 610 | ], 611 | "authors": [ 612 | { 613 | "name": "Sebastian Bergmann", 614 | "email": "sb@sebastian-bergmann.de", 615 | "role": "lead" 616 | } 617 | ], 618 | "description": "Wrapper around PHP's tokenizer extension.", 619 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 620 | "keywords": [ 621 | "tokenizer" 622 | ], 623 | "time": "2013-09-13 04:58:23" 624 | }, 625 | { 626 | "name": "phpunit/phpunit", 627 | "version": "3.7.28", 628 | "source": { 629 | "type": "git", 630 | "url": "https://github.com/sebastianbergmann/phpunit.git", 631 | "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d" 632 | }, 633 | "dist": { 634 | "type": "zip", 635 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", 636 | "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", 637 | "shasum": "" 638 | }, 639 | "require": { 640 | "ext-dom": "*", 641 | "ext-pcre": "*", 642 | "ext-reflection": "*", 643 | "ext-spl": "*", 644 | "php": ">=5.3.3", 645 | "phpunit/php-code-coverage": "~1.2.1", 646 | "phpunit/php-file-iterator": ">=1.3.1", 647 | "phpunit/php-text-template": ">=1.1.1", 648 | "phpunit/php-timer": ">=1.0.4", 649 | "phpunit/phpunit-mock-objects": "~1.2.0", 650 | "symfony/yaml": "~2.0" 651 | }, 652 | "require-dev": { 653 | "pear-pear/pear": "1.9.4" 654 | }, 655 | "suggest": { 656 | "ext-json": "*", 657 | "ext-simplexml": "*", 658 | "ext-tokenizer": "*", 659 | "phpunit/php-invoker": ">=1.1.0,<1.2.0" 660 | }, 661 | "bin": [ 662 | "composer/bin/phpunit" 663 | ], 664 | "type": "library", 665 | "extra": { 666 | "branch-alias": { 667 | "dev-master": "3.7.x-dev" 668 | } 669 | }, 670 | "autoload": { 671 | "classmap": [ 672 | "PHPUnit/" 673 | ] 674 | }, 675 | "notification-url": "https://packagist.org/downloads/", 676 | "include-path": [ 677 | "", 678 | "../../symfony/yaml/" 679 | ], 680 | "license": [ 681 | "BSD-3-Clause" 682 | ], 683 | "authors": [ 684 | { 685 | "name": "Sebastian Bergmann", 686 | "email": "sebastian@phpunit.de", 687 | "role": "lead" 688 | } 689 | ], 690 | "description": "The PHP Unit Testing framework.", 691 | "homepage": "http://www.phpunit.de/", 692 | "keywords": [ 693 | "phpunit", 694 | "testing", 695 | "xunit" 696 | ], 697 | "time": "2013-10-17 07:27:40" 698 | }, 699 | { 700 | "name": "phpunit/phpunit-mock-objects", 701 | "version": "1.2.3", 702 | "source": { 703 | "type": "git", 704 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 705 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" 706 | }, 707 | "dist": { 708 | "type": "zip", 709 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", 710 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", 711 | "shasum": "" 712 | }, 713 | "require": { 714 | "php": ">=5.3.3", 715 | "phpunit/php-text-template": ">=1.1.1@stable" 716 | }, 717 | "suggest": { 718 | "ext-soap": "*" 719 | }, 720 | "type": "library", 721 | "autoload": { 722 | "classmap": [ 723 | "PHPUnit/" 724 | ] 725 | }, 726 | "notification-url": "https://packagist.org/downloads/", 727 | "include-path": [ 728 | "" 729 | ], 730 | "license": [ 731 | "BSD-3-Clause" 732 | ], 733 | "authors": [ 734 | { 735 | "name": "Sebastian Bergmann", 736 | "email": "sb@sebastian-bergmann.de", 737 | "role": "lead" 738 | } 739 | ], 740 | "description": "Mock Object library for PHPUnit", 741 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 742 | "keywords": [ 743 | "mock", 744 | "xunit" 745 | ], 746 | "time": "2013-01-13 10:24:48" 747 | }, 748 | { 749 | "name": "silex/silex", 750 | "version": "dev-master", 751 | "source": { 752 | "type": "git", 753 | "url": "https://github.com/silexphp/Silex.git", 754 | "reference": "71661809247193080e77c0613415f0df15edb485" 755 | }, 756 | "dist": { 757 | "type": "zip", 758 | "url": "https://api.github.com/repos/silexphp/Silex/zipball/71661809247193080e77c0613415f0df15edb485", 759 | "reference": "71661809247193080e77c0613415f0df15edb485", 760 | "shasum": "" 761 | }, 762 | "require": { 763 | "php": ">=5.3.3", 764 | "pimple/pimple": "~1.0", 765 | "symfony/event-dispatcher": ">=2.3,<2.5-dev", 766 | "symfony/http-foundation": ">=2.3,<2.5-dev", 767 | "symfony/http-kernel": ">=2.3,<2.5-dev", 768 | "symfony/routing": ">=2.3,<2.5-dev" 769 | }, 770 | "require-dev": { 771 | "doctrine/dbal": ">=2.2.0,<2.4.0-dev", 772 | "monolog/monolog": "~1.4,>=1.4.1", 773 | "phpunit/phpunit": "~3.7", 774 | "swiftmailer/swiftmailer": "5.*", 775 | "symfony/browser-kit": ">=2.3,<2.5-dev", 776 | "symfony/config": ">=2.3,<2.5-dev", 777 | "symfony/css-selector": ">=2.3,<2.5-dev", 778 | "symfony/debug": ">=2.3,<2.5-dev", 779 | "symfony/dom-crawler": ">=2.3,<2.5-dev", 780 | "symfony/finder": ">=2.3,<2.5-dev", 781 | "symfony/form": ">=2.3,<2.5-dev", 782 | "symfony/locale": ">=2.3,<2.5-dev", 783 | "symfony/monolog-bridge": ">=2.3,<2.5-dev", 784 | "symfony/options-resolver": ">=2.3,<2.5-dev", 785 | "symfony/process": ">=2.3,<2.5-dev", 786 | "symfony/security": ">=2.3,<2.5-dev", 787 | "symfony/serializer": ">=2.3,<2.5-dev", 788 | "symfony/translation": ">=2.3,<2.5-dev", 789 | "symfony/twig-bridge": ">=2.3,<2.5-dev", 790 | "symfony/validator": ">=2.3,<2.5-dev", 791 | "twig/twig": ">=1.8.0,<2.0-dev" 792 | }, 793 | "suggest": { 794 | "symfony/browser-kit": ">=2.3,<2.5-dev", 795 | "symfony/css-selector": ">=2.3,<2.5-dev", 796 | "symfony/dom-crawler": ">=2.3,<2.5-dev", 797 | "symfony/form": ">=2.3,<2.5-dev" 798 | }, 799 | "type": "library", 800 | "extra": { 801 | "branch-alias": { 802 | "dev-master": "1.2.x-dev" 803 | } 804 | }, 805 | "autoload": { 806 | "psr-0": { 807 | "Silex": "src/" 808 | } 809 | }, 810 | "notification-url": "https://packagist.org/downloads/", 811 | "license": [ 812 | "MIT" 813 | ], 814 | "authors": [ 815 | { 816 | "name": "Fabien Potencier", 817 | "email": "fabien@symfony.com" 818 | }, 819 | { 820 | "name": "Igor Wiedler", 821 | "email": "igor@wiedler.ch", 822 | "homepage": "http://wiedler.ch/igor/" 823 | } 824 | ], 825 | "description": "The PHP micro-framework based on the Symfony2 Components", 826 | "homepage": "http://silex.sensiolabs.org", 827 | "keywords": [ 828 | "microframework" 829 | ], 830 | "time": "2013-11-11 19:01:23" 831 | }, 832 | { 833 | "name": "stack/builder", 834 | "version": "dev-master", 835 | "source": { 836 | "type": "git", 837 | "url": "https://github.com/stackphp/builder.git", 838 | "reference": "49ab90450d7f959943f3659a4bcb5965530117c2" 839 | }, 840 | "dist": { 841 | "type": "zip", 842 | "url": "https://api.github.com/repos/stackphp/builder/zipball/49ab90450d7f959943f3659a4bcb5965530117c2", 843 | "reference": "49ab90450d7f959943f3659a4bcb5965530117c2", 844 | "shasum": "" 845 | }, 846 | "require": { 847 | "php": ">=5.3.0", 848 | "symfony/http-foundation": "~2.1", 849 | "symfony/http-kernel": "~2.1" 850 | }, 851 | "require-dev": { 852 | "silex/silex": "~1.0" 853 | }, 854 | "type": "library", 855 | "extra": { 856 | "branch-alias": { 857 | "dev-master": "1.0-dev" 858 | } 859 | }, 860 | "autoload": { 861 | "psr-0": { 862 | "Stack": "src" 863 | } 864 | }, 865 | "notification-url": "https://packagist.org/downloads/", 866 | "license": [ 867 | "MIT" 868 | ], 869 | "authors": [ 870 | { 871 | "name": "Igor Wiedler", 872 | "email": "igor@wiedler.ch", 873 | "homepage": "http://wiedler.ch/igor/" 874 | } 875 | ], 876 | "description": "Builder for stack middlewares based on HttpKernelInterface.", 877 | "keywords": [ 878 | "stack" 879 | ], 880 | "time": "2013-10-25 14:04:45" 881 | }, 882 | { 883 | "name": "stack/callable-http-kernel", 884 | "version": "dev-master", 885 | "source": { 886 | "type": "git", 887 | "url": "https://github.com/stackphp/CallableHttpKernel.git", 888 | "reference": "97f6bd2aaf0f173a902dfd6bd4ffa558a799531d" 889 | }, 890 | "dist": { 891 | "type": "zip", 892 | "url": "https://api.github.com/repos/stackphp/CallableHttpKernel/zipball/97f6bd2aaf0f173a902dfd6bd4ffa558a799531d", 893 | "reference": "97f6bd2aaf0f173a902dfd6bd4ffa558a799531d", 894 | "shasum": "" 895 | }, 896 | "require": { 897 | "php": ">=5.3.0", 898 | "symfony/http-foundation": "~2.1", 899 | "symfony/http-kernel": "~2.1" 900 | }, 901 | "type": "library", 902 | "extra": { 903 | "branch-alias": { 904 | "dev-master": "1.0-dev" 905 | } 906 | }, 907 | "autoload": { 908 | "psr-0": { 909 | "Stack": "src" 910 | } 911 | }, 912 | "notification-url": "https://packagist.org/downloads/", 913 | "license": [ 914 | "MIT" 915 | ], 916 | "authors": [ 917 | { 918 | "name": "Igor Wiedler", 919 | "email": "igor@wiedler.ch", 920 | "homepage": "http://wiedler.ch/igor/" 921 | } 922 | ], 923 | "description": "HttpKernelInterface implementation based on callables.", 924 | "keywords": [ 925 | "stack" 926 | ], 927 | "time": "2013-10-25 14:27:17" 928 | }, 929 | { 930 | "name": "stack/session", 931 | "version": "dev-master", 932 | "source": { 933 | "type": "git", 934 | "url": "https://github.com/stackphp/session.git", 935 | "reference": "117c2d01d283fbd0f0ba6582687d0b028834a7a0" 936 | }, 937 | "dist": { 938 | "type": "zip", 939 | "url": "https://api.github.com/repos/stackphp/session/zipball/117c2d01d283fbd0f0ba6582687d0b028834a7a0", 940 | "reference": "117c2d01d283fbd0f0ba6582687d0b028834a7a0", 941 | "shasum": "" 942 | }, 943 | "require": { 944 | "php": ">=5.4.0", 945 | "pimple/pimple": "~1.0@dev", 946 | "symfony/http-foundation": "~2.1", 947 | "symfony/http-kernel": "~2.1" 948 | }, 949 | "require-dev": { 950 | "silex/silex": "~1.0@dev", 951 | "stack/builder": "~1.0@dev", 952 | "stack/callable-http-kernel": "~1.0@dev", 953 | "symfony/browser-kit": "~2.1" 954 | }, 955 | "type": "library", 956 | "extra": { 957 | "branch-alias": { 958 | "dev-master": "1.0-dev" 959 | } 960 | }, 961 | "autoload": { 962 | "psr-0": { 963 | "Stack": "src" 964 | } 965 | }, 966 | "notification-url": "https://packagist.org/downloads/", 967 | "license": [ 968 | "MIT" 969 | ], 970 | "authors": [ 971 | { 972 | "name": "Igor Wiedler", 973 | "email": "igor@wiedler.ch", 974 | "homepage": "http://wiedler.ch/igor/" 975 | } 976 | ], 977 | "description": "Session stack middleware.", 978 | "keywords": [ 979 | "session", 980 | "stack" 981 | ], 982 | "time": "2013-06-23 20:58:54" 983 | }, 984 | { 985 | "name": "symfony/routing", 986 | "version": "v2.3.6", 987 | "target-dir": "Symfony/Component/Routing", 988 | "source": { 989 | "type": "git", 990 | "url": "https://github.com/symfony/Routing.git", 991 | "reference": "7d41463094752e87a0fae60316d236abecb8a034" 992 | }, 993 | "dist": { 994 | "type": "zip", 995 | "url": "https://api.github.com/repos/symfony/Routing/zipball/7d41463094752e87a0fae60316d236abecb8a034", 996 | "reference": "7d41463094752e87a0fae60316d236abecb8a034", 997 | "shasum": "" 998 | }, 999 | "require": { 1000 | "php": ">=5.3.3" 1001 | }, 1002 | "require-dev": { 1003 | "doctrine/common": "~2.2", 1004 | "psr/log": "~1.0", 1005 | "symfony/config": "~2.2", 1006 | "symfony/yaml": "~2.0" 1007 | }, 1008 | "suggest": { 1009 | "doctrine/common": "", 1010 | "symfony/config": "", 1011 | "symfony/yaml": "" 1012 | }, 1013 | "type": "library", 1014 | "extra": { 1015 | "branch-alias": { 1016 | "dev-master": "2.3-dev" 1017 | } 1018 | }, 1019 | "autoload": { 1020 | "psr-0": { 1021 | "Symfony\\Component\\Routing\\": "" 1022 | } 1023 | }, 1024 | "notification-url": "https://packagist.org/downloads/", 1025 | "license": [ 1026 | "MIT" 1027 | ], 1028 | "authors": [ 1029 | { 1030 | "name": "Fabien Potencier", 1031 | "email": "fabien@symfony.com" 1032 | }, 1033 | { 1034 | "name": "Symfony Community", 1035 | "homepage": "http://symfony.com/contributors" 1036 | } 1037 | ], 1038 | "description": "Symfony Routing Component", 1039 | "homepage": "http://symfony.com", 1040 | "time": "2013-09-29 19:41:41" 1041 | }, 1042 | { 1043 | "name": "symfony/yaml", 1044 | "version": "v2.3.6", 1045 | "target-dir": "Symfony/Component/Yaml", 1046 | "source": { 1047 | "type": "git", 1048 | "url": "https://github.com/symfony/Yaml.git", 1049 | "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3" 1050 | }, 1051 | "dist": { 1052 | "type": "zip", 1053 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/6bb881b948368482e1abf1a75c08bcf88a1c5fc3", 1054 | "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3", 1055 | "shasum": "" 1056 | }, 1057 | "require": { 1058 | "php": ">=5.3.3" 1059 | }, 1060 | "type": "library", 1061 | "extra": { 1062 | "branch-alias": { 1063 | "dev-master": "2.3-dev" 1064 | } 1065 | }, 1066 | "autoload": { 1067 | "psr-0": { 1068 | "Symfony\\Component\\Yaml\\": "" 1069 | } 1070 | }, 1071 | "notification-url": "https://packagist.org/downloads/", 1072 | "license": [ 1073 | "MIT" 1074 | ], 1075 | "authors": [ 1076 | { 1077 | "name": "Fabien Potencier", 1078 | "email": "fabien@symfony.com" 1079 | }, 1080 | { 1081 | "name": "Symfony Community", 1082 | "homepage": "http://symfony.com/contributors" 1083 | } 1084 | ], 1085 | "description": "Symfony Yaml Component", 1086 | "homepage": "http://symfony.com", 1087 | "time": "2013-09-22 18:04:39" 1088 | } 1089 | ], 1090 | "aliases": [ 1091 | 1092 | ], 1093 | "minimum-stability": "stable", 1094 | "stability-flags": { 1095 | "pimple/pimple": 20, 1096 | "silex/silex": 20, 1097 | "stack/builder": 20, 1098 | "stack/session": 20, 1099 | "stack/callable-http-kernel": 20 1100 | }, 1101 | "platform": { 1102 | "php": ">=5.4.0" 1103 | }, 1104 | "platform-dev": [ 1105 | 1106 | ] 1107 | } 1108 | -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | attributes->get('oauth.token'); 11 | 12 | if (!$token) { 13 | return new RedirectResponse('/auth'); 14 | } 15 | 16 | $params = $token->getExtraParams(); 17 | $body = sprintf('Welcome @%s!', $params['screen_name']); 18 | 19 | return new Response($body); 20 | }); 21 | 22 | $stack = (new Stack\Builder()) 23 | ->push('Stack\Session') 24 | ->push('Igorw\Stack\OAuth', [ 25 | 'key' => 'foo', 26 | 'secret' => 'bar', 27 | 'callback_url' => 'http://localhost:8080/auth/verify', 28 | 'success_url' => '/', 29 | 'failure_url' => '/auth', 30 | ]); 31 | 32 | $app = $stack->resolve($app); 33 | 34 | $request = Request::createFromGlobals(); 35 | $response = $app->handle($request)->send(); 36 | $app->terminate($request, $response); 37 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | ./tests/unit/ 10 | 11 | 12 | 13 | 14 | 15 | ./tests/integration/ 16 | 17 | 18 | 19 | 20 | 21 | ./tests/functional/ 22 | 23 | 24 | 25 | 26 | 27 | ./src/ 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/Igorw/Stack/OAuth.php: -------------------------------------------------------------------------------- 1 | app = $app; 19 | $this->container = $this->setupContainer($options); 20 | } 21 | 22 | public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) 23 | { 24 | $this->container['session'] = $request->getSession(); 25 | 26 | $controller = $this->container['router']->match($request); 27 | if ($controller) { 28 | return $controller($request); 29 | } 30 | 31 | try { 32 | $token = $this->container['storage']->retrieveAccessToken(); 33 | } catch (TokenNotFoundException $e) { 34 | $token = null; 35 | } 36 | 37 | $request->attributes->set('oauth.token', $token); 38 | $request->attributes->set( 39 | 'stack.authn.token', 40 | $this->container['token_translator']($token) 41 | ); 42 | 43 | return $this->app->handle($request, $type, $catch); 44 | } 45 | 46 | private function setupContainer(array $options) 47 | { 48 | $container = new Pimple(); 49 | 50 | $config = new ContainerConfig(); 51 | $config->process($container); 52 | 53 | foreach ($options as $name => $value) { 54 | if (in_array($name, ['token_translator'])) { 55 | $container[$name] = $container->share(function () use ($value) { 56 | return $value; 57 | }); 58 | } else { 59 | $container[$name] = $value; 60 | } 61 | } 62 | 63 | return $container; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Igorw/Stack/OAuth/AuthController.php: -------------------------------------------------------------------------------- 1 | storage = $storage; 21 | $this->oauth = $oauth; 22 | $this->successUrl = $successUrl; 23 | $this->failureUrl = $failureUrl; 24 | } 25 | 26 | public function authAction(Request $request) 27 | { 28 | $token = $this->oauth->requestRequestToken(); 29 | $url = $this->oauth->getAuthorizationUri(['oauth_token' => $token->getRequestToken()]); 30 | 31 | $request->getSession()->set('oauth.success_url', $request->headers->get('Referer')); 32 | 33 | return new RedirectResponse($url->getAbsoluteUri(), 302, ['Cache-Control' => 'no-cache']); 34 | } 35 | 36 | public function verifyAction(Request $request) 37 | { 38 | try { 39 | $token = $this->storage->retrieveAccessToken(); 40 | $token = $this->oauth->requestAccessToken( 41 | $request->query->get('oauth_token'), 42 | $request->query->get('oauth_verifier'), 43 | $token->getRequestTokenSecret() 44 | ); 45 | $userId = (int) $token->getExtraParams()['user_id']; 46 | 47 | $successUrl = $this->successUrl ?: $request->getSession()->get('oauth.success_url'); 48 | $request->getSession()->remove('oauth.success_url'); 49 | 50 | if (!$successUrl) { 51 | throw new \RuntimeException('Did not find oauth.success_url.'); 52 | } 53 | 54 | return new RedirectResponse($successUrl); 55 | } catch (TokenResponseException $e) { 56 | // TODO: figure out some sane way to log this 57 | error_log($e->getMessage()); 58 | 59 | return new RedirectResponse($this->failureUrl); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Igorw/Stack/OAuth/ContainerConfig.php: -------------------------------------------------------------------------------- 1 | share(function ($container) { 18 | return new SymfonySession($container['session']); 19 | }); 20 | 21 | $container['credentials'] = $container->share(function ($container) { 22 | return new Credentials( 23 | $container['key'], 24 | $container['secret'], 25 | $container['callback_url'] 26 | ); 27 | }); 28 | 29 | $container['signature'] = $container->share(function ($container) { 30 | return new Signature($container['credentials']); 31 | }); 32 | 33 | $container['http_client'] = $container->share(function () { 34 | return new StreamClient(); 35 | }); 36 | 37 | $container['oauth_service.class'] = 'OAuth\OAuth1\Service\Twitter'; 38 | 39 | $container['oauth_service'] = $container->share(function ($container) { 40 | return new $container['oauth_service.class']( 41 | $container['credentials'], 42 | $container['http_client'], 43 | $container['storage'], 44 | $container['signature'] 45 | ); 46 | }); 47 | 48 | $container['auth_controller'] = $container->share(function ($container) { 49 | return new AuthController( 50 | $container['storage'], 51 | $container['oauth_service'], 52 | $container['success_url'], 53 | $container['failure_url'] 54 | ); 55 | }); 56 | 57 | $container['routes'] = [ 58 | '/auth' => 'auth_controller:authAction', 59 | '/auth/verify' => 'auth_controller:verifyAction', 60 | ]; 61 | 62 | $container['router'] = $container->share(function ($container) { 63 | return new Router($container, $container['routes']); 64 | }); 65 | 66 | $container['success_url'] = null; 67 | 68 | $container['token_translator'] = $container->protect(function ($token) { 69 | return $token; 70 | }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Igorw/Stack/Router.php: -------------------------------------------------------------------------------- 1 | container = $container; 16 | $this->map = $map; 17 | } 18 | 19 | public function match(Request $request) 20 | { 21 | foreach ($this->map as $path => $definition) { 22 | if ($path === $request->getPathInfo()) { 23 | return $this->getController($definition); 24 | } 25 | } 26 | 27 | return null; 28 | } 29 | 30 | private function getController($definition) 31 | { 32 | list($service, $action) = explode(':', $definition); 33 | $controller = $this->container[$service]; 34 | 35 | return [$controller, $action]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('common', __DIR__); 5 | --------------------------------------------------------------------------------