| @@ -0,0 +1,21 @@ | ||
|
|
||
| Copyright (c) 2015 Nils Adermann, Jordi Boggiano | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is furnished | ||
| to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| THE SOFTWARE. | ||
|
|
| @@ -0,0 +1,26 @@ | ||
| <?php | ||
|
|
||
| // autoload_classmap.php @generated by Composer | ||
|
|
||
| $vendorDir = dirname(dirname(__FILE__)); | ||
| $baseDir = dirname($vendorDir); | ||
|
|
||
| return array( | ||
| 'App\\M\\BaseModel' => $baseDir . '/application/_common/model/mysql/BaseModel.php', | ||
| 'App\\M\\Defect' => $baseDir . '/application/_common/model/mysql/Defect.php', | ||
| 'App\\M\\Group' => $baseDir . '/application/_common/model/mysql/Group.php', | ||
| 'App\\M\\ManageType' => $baseDir . '/application/_common/model/mysql/ManageType.php', | ||
| 'App\\M\\Plate' => $baseDir . '/application/_common/model/mysql/Plate.php', | ||
| 'App\\M\\Resource' => $baseDir . '/application/_common/model/mysql/Resource.php', | ||
| 'App\\M\\Test' => $baseDir . '/application/_common/model/mysql/Test.php', | ||
| 'App\\M\\User' => $baseDir . '/application/_common/model/mysql/User.php', | ||
| 'Curl' => $baseDir . '/library/extends/Curl.php', | ||
| 'Library\\Extend\\Hash' => $baseDir . '/library/extends/Hash.php', | ||
| 'Library\\Extend\\Img' => $baseDir . '/library/extends/Img.php', | ||
| 'Library\\Extend\\Tree' => $baseDir . '/library/extends/Tree.php', | ||
| 'Library\\Extend\\Upload' => $baseDir . '/library/extends/Upload.php', | ||
| 'Library\\Extend\\Url' => $baseDir . '/library/extends/Url.php', | ||
| 'Whoops\\Module' => $vendorDir . '/filp/whoops/src/deprecated/Zend/Module.php', | ||
| 'Whoops\\Provider\\Zend\\ExceptionStrategy' => $vendorDir . '/filp/whoops/src/deprecated/Zend/ExceptionStrategy.php', | ||
| 'Whoops\\Provider\\Zend\\RouteNotFoundStrategy' => $vendorDir . '/filp/whoops/src/deprecated/Zend/RouteNotFoundStrategy.php', | ||
| ); |
| @@ -0,0 +1,11 @@ | ||
| <?php | ||
|
|
||
| // autoload_files.php @generated by Composer | ||
|
|
||
| $vendorDir = dirname(dirname(__FILE__)); | ||
| $baseDir = dirname($vendorDir); | ||
|
|
||
| return array( | ||
| '9b24cdf6705c981dc519077514baadf4' => $vendorDir . '/danielstjules/stringy/src/Create.php', | ||
| 'fe03086da41e24b75dac209691cb9360' => $vendorDir . '/illuminate/support/helpers.php', | ||
| ); |
| @@ -0,0 +1,11 @@ | ||
| <?php | ||
|
|
||
| // autoload_namespaces.php @generated by Composer | ||
|
|
||
| $vendorDir = dirname(dirname(__FILE__)); | ||
| $baseDir = dirname($vendorDir); | ||
|
|
||
| return array( | ||
| 'Whoops' => array($vendorDir . '/filp/whoops/src'), | ||
| 'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib'), | ||
| ); |
| @@ -0,0 +1,16 @@ | ||
| <?php | ||
|
|
||
| // autoload_psr4.php @generated by Composer | ||
|
|
||
| $vendorDir = dirname(dirname(__FILE__)); | ||
| $baseDir = dirname($vendorDir); | ||
|
|
||
| return array( | ||
| 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), | ||
| 'Stringy\\' => array($vendorDir . '/danielstjules/stringy/src'), | ||
| 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/support'), | ||
| 'Illuminate\\Database\\' => array($vendorDir . '/illuminate/database'), | ||
| 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'), | ||
| 'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'), | ||
| 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'), | ||
| ); |
| @@ -0,0 +1,59 @@ | ||
| <?php | ||
|
|
||
| // autoload_real.php @generated by Composer | ||
|
|
||
| class ComposerAutoloaderInit5fe966925672043ad81cc30b294e09b3 | ||
| { | ||
| private static $loader; | ||
|
|
||
| public static function loadClassLoader($class) | ||
| { | ||
| if ('Composer\Autoload\ClassLoader' === $class) { | ||
| require __DIR__ . '/ClassLoader.php'; | ||
| } | ||
| } | ||
|
|
||
| public static function getLoader() | ||
| { | ||
| if (null !== self::$loader) { | ||
| return self::$loader; | ||
| } | ||
|
|
||
| spl_autoload_register(array('ComposerAutoloaderInit5fe966925672043ad81cc30b294e09b3', 'loadClassLoader'), true, true); | ||
| self::$loader = $loader = new \Composer\Autoload\ClassLoader(); | ||
| spl_autoload_unregister(array('ComposerAutoloaderInit5fe966925672043ad81cc30b294e09b3', 'loadClassLoader')); | ||
|
|
||
| $map = require __DIR__ . '/autoload_namespaces.php'; | ||
| foreach ($map as $namespace => $path) { | ||
| $loader->set($namespace, $path); | ||
| } | ||
|
|
||
| $map = require __DIR__ . '/autoload_psr4.php'; | ||
| foreach ($map as $namespace => $path) { | ||
| $loader->setPsr4($namespace, $path); | ||
| } | ||
|
|
||
| $classMap = require __DIR__ . '/autoload_classmap.php'; | ||
| if ($classMap) { | ||
| $loader->addClassMap($classMap); | ||
| } | ||
|
|
||
| $loader->register(true); | ||
|
|
||
| $includeFiles = require __DIR__ . '/autoload_files.php'; | ||
| foreach ($includeFiles as $fileIdentifier => $file) { | ||
| composerRequire5fe966925672043ad81cc30b294e09b3($fileIdentifier, $file); | ||
| } | ||
|
|
||
| return $loader; | ||
| } | ||
| } | ||
|
|
||
| function composerRequire5fe966925672043ad81cc30b294e09b3($fileIdentifier, $file) | ||
| { | ||
| if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { | ||
| require $file; | ||
|
|
||
| $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; | ||
| } | ||
| } |
| @@ -0,0 +1,3 @@ | ||
| vendor/ | ||
| composer.lock | ||
| .DS_Store |
| @@ -0,0 +1,7 @@ | ||
| language: php | ||
| php: | ||
| - 5.6 | ||
| - 5.5 | ||
| - 5.4 | ||
| - 5.3 | ||
| - hhvm |
| @@ -0,0 +1,119 @@ | ||
| ### 1.10.0 (2015-07-22) | ||
|
|
||
| * Added trimLeft, trimRight | ||
| * Added support for unicode whitespace to trim | ||
| * Added delimit | ||
| * Added indexOf and indexOfLast | ||
| * Added htmlEncode and htmlDecode | ||
| * Added "Ç" in toAscii() | ||
|
|
||
| ### 1.9.0 (2015-02-09) | ||
|
|
||
| * Added hasUpperCase and hasLowerCase | ||
| * Added $removeUnsupported parameter to toAscii() | ||
| * Improved toAscii support with additional Unicode spaces, Vietnamese chars, | ||
| and numerous other characters | ||
| * Separated the charsArray from toAscii as a protected method that may be | ||
| extended by inheriting classes | ||
| * Chars array is cached for better performance | ||
|
|
||
| ### 1.8.1 (2015-01-08) | ||
|
|
||
| * Optimized chars() | ||
| * Added "ä Ä Ö Ü"" in toAscii() | ||
| * Added support for Unicode spaces in toAscii() | ||
| * Replaced instances of self::create() with static::create() | ||
| * Added missing test cases for safeTruncate() and longestCommonSuffix() | ||
| * Updated Stringy\create() to avoid collision when it already exists | ||
|
|
||
| ### 1.8.0 (2015-01-03) | ||
|
|
||
| * Listed ext-mbstring in composer.json | ||
| * Added Stringy\create function for PHP 5.6 | ||
|
|
||
| ### 1.7.0 (2014-10-14) | ||
|
|
||
| * Added containsAll and containsAny | ||
| * Light cleanup | ||
|
|
||
| ### 1.6.0 (2014-09-14) | ||
|
|
||
| * Added toTitleCase | ||
|
|
||
| ### 1.5.2 (2014-07-09) | ||
|
|
||
| * Announced support for HHVM | ||
|
|
||
| ### 1.5.1 (2014-04-19) | ||
|
|
||
| * Fixed toAscii() failing to remove remaining non-ascii characters | ||
| * Updated slugify() to treat dash and underscore as delimiters by default | ||
| * Updated slugify() to remove leading and trailing delimiter, if present | ||
|
|
||
| ### 1.5.0 (2014-03-19) | ||
|
|
||
| * Made both str and encoding protected, giving property access to subclasses | ||
| * Added getEncoding() | ||
| * Fixed isJSON() giving false negatives | ||
| * Cleaned up and simplified: replace(), collapseWhitespace(), underscored(), | ||
| dasherize(), pad(), padLeft(), padRight() and padBoth() | ||
| * Fixed handling consecutive invalid chars in slugify() | ||
| * Removed conflicting hard sign transliteration in toAscii() | ||
|
|
||
| ### 1.4.0 (2014-02-12) | ||
|
|
||
| * Implemented the IteratorAggregate interface, added chars() | ||
| * Renamed count() to countSubstr() | ||
| * Updated count() to implement Countable interface | ||
| * Implemented the ArrayAccess interface with positive and negative indices | ||
| * Switched from PSR-0 to PSR-4 autoloading | ||
|
|
||
| ### 1.3.0 (2013-12-16) | ||
|
|
||
| * Additional Bulgarian support for toAscii | ||
| * str property made private | ||
| * Constructor casts first argument to string | ||
| * Constructor throws an InvalidArgumentException when given an array | ||
| * Constructor throws an InvalidArgumentException when given an object without | ||
| a __toString method | ||
|
|
||
| ### 1.2.2 (2013-12-04) | ||
|
|
||
| * Updated create function to use late static binding | ||
| * Added optional $replacement param to slugify | ||
|
|
||
| ### 1.2.1 (2013-10-11) | ||
|
|
||
| * Cleaned up tests | ||
| * Added homepage to composer.json | ||
|
|
||
| ### 1.2.0 (2013-09-15) | ||
|
|
||
| * Fixed pad's use of InvalidArgumentException | ||
| * Fixed replace(). It now correctly treats regex special chars as normal chars | ||
| * Added additional Cyrillic letters to toAscii | ||
| * Added $caseSensitive to contains() and count() | ||
| * Added toLowerCase() | ||
| * Added toUpperCase() | ||
| * Added regexReplace() | ||
|
|
||
| ### 1.1.0 (2013-08-31) | ||
|
|
||
| * Fix for collapseWhitespace() | ||
| * Added isHexadecimal() | ||
| * Added constructor to Stringy\Stringy | ||
| * Added isSerialized() | ||
| * Added isJson() | ||
|
|
||
| ### 1.0.0 (2013-08-1) | ||
|
|
||
| * 1.0.0 release | ||
| * Added test coverage for Stringy::create and method chaining | ||
| * Added tests for returned type | ||
| * Fixed StaticStringy::replace(). It was returning a Stringy object instead of string | ||
| * Renamed standardize() to the more appropriate toAscii() | ||
| * Cleaned up comments and README | ||
|
|
||
| ### 1.0.0-rc.1 (2013-07-28) | ||
|
|
||
| * Release candidate |
| @@ -0,0 +1,19 @@ | ||
| Copyright (C) 2013 Daniel St. Jules | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| THE SOFTWARE. |
| @@ -0,0 +1,35 @@ | ||
| { | ||
| "name": "danielstjules/stringy", | ||
| "description": "A string manipulation library with multibyte support", | ||
| "keywords": [ | ||
| "multibyte", "string", "manipulation", "utility", "methods", "utf-8", | ||
| "helpers", "utils", "utf" | ||
| ], | ||
| "homepage": "https://github.com/danielstjules/Stringy", | ||
| "license": "MIT", | ||
| "authors": [ | ||
| { | ||
| "name": "Daniel St. Jules", | ||
| "email": "danielst.jules@gmail.com", | ||
| "homepage": "http://www.danielstjules.com" | ||
| } | ||
| ], | ||
| "require": { | ||
| "php": ">=5.3.0", | ||
| "ext-mbstring": "*" | ||
| }, | ||
| "require-dev": { | ||
| "phpunit/phpunit": "~4.0" | ||
| }, | ||
| "support": { | ||
| "issues": "https://github.com/danielstjules/Stringy/issues", | ||
| "source": "https://github.com/danielstjules/Stringy" | ||
| }, | ||
| "autoload": { | ||
| "psr-4": { "Stringy\\": "src/" }, | ||
| "files": ["src/Create.php"] | ||
| }, | ||
| "autoload-dev": { | ||
| "classmap": [ "tests" ] | ||
| } | ||
| } |
| @@ -0,0 +1,14 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
|
|
||
| <phpunit processIsolation="false" | ||
| stopOnFailure="false" | ||
| syntaxCheck="false"> | ||
| <testsuites> | ||
| <testsuite name="Stringy"> | ||
| <file>tests/CommonTest.php</file> | ||
| <file>tests/StringyTest.php</file> | ||
| <file>tests/StaticStringyTest.php</file> | ||
| <file phpVersion="5.6.0">tests/CreateTest.php</file> | ||
| </testsuite> | ||
| </testsuites> | ||
| </phpunit> |
| @@ -0,0 +1,19 @@ | ||
| <?php | ||
|
|
||
| namespace Stringy; | ||
|
|
||
| if (!function_exists('Stringy\create')) { | ||
| /** | ||
| * Creates a Stringy object and returns it on success. | ||
| * | ||
| * @param mixed $str Value to modify, after being cast to string | ||
| * @param string $encoding The character encoding | ||
| * @return Stringy A Stringy object | ||
| * @throws \InvalidArgumentException if an array or object without a | ||
| * __toString method is passed as the first argument | ||
| */ | ||
| function create($str, $encoding = null) | ||
| { | ||
| return new Stringy($str, $encoding); | ||
| } | ||
| } |
| @@ -0,0 +1,16 @@ | ||
| <?php | ||
|
|
||
| require __DIR__ . '/../src/Create.php'; | ||
|
|
||
| use function Stringy\create as s; | ||
|
|
||
| class CreateTestCase extends PHPUnit_Framework_TestCase | ||
| { | ||
| public function testCreate() | ||
| { | ||
| $stringy = s('foo bar', 'UTF-8'); | ||
| $this->assertInstanceOf('Stringy\Stringy', $stringy); | ||
| $this->assertEquals('foo bar', (string) $stringy); | ||
| $this->assertEquals('UTF-8', $stringy->getEncoding()); | ||
| } | ||
| } |
| @@ -0,0 +1,4 @@ | ||
| vendor/ | ||
| composer.lock | ||
| composer.phar | ||
| phpunit.xml |
| @@ -0,0 +1,21 @@ | ||
| language: php | ||
|
|
||
| sudo: false | ||
|
|
||
| cache: | ||
| directory: | ||
| - $HOME/.composer/cache | ||
|
|
||
| php: | ||
| - 5.3 | ||
| - 5.4 | ||
| - 5.5 | ||
| - 5.6 | ||
| - 7.0 | ||
| - hhvm | ||
|
|
||
| install: | ||
| - composer install -n | ||
|
|
||
| script: | ||
| - phpunit |
| @@ -0,0 +1,19 @@ | ||
| Copyright (c) 2006-2015 Doctrine Project | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| this software and associated documentation files (the "Software"), to deal in | ||
| the Software without restriction, including without limitation the rights to | ||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| of the Software, and to permit persons to whom the Software is furnished to do | ||
| so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| @@ -0,0 +1,6 @@ | ||
| # Doctrine Inflector | ||
|
|
||
| Doctrine Inflector is a small library that can perform string manipulations | ||
| with regard to upper-/lowercase and singular/plural forms of words. | ||
|
|
||
| [](https://travis-ci.org/doctrine/inflector) |
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "name": "doctrine/inflector", | ||
| "type": "library", | ||
| "description": "Common String Manipulations with regard to casing and singular/plural rules.", | ||
| "keywords": ["string", "inflection", "singularize", "pluralize"], | ||
| "homepage": "http://www.doctrine-project.org", | ||
| "license": "MIT", | ||
| "authors": [ | ||
| {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, | ||
| {"name": "Roman Borschel", "email": "roman@code-factory.org"}, | ||
| {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, | ||
| {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, | ||
| {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} | ||
| ], | ||
| "require": { | ||
| "php": ">=5.3.2" | ||
| }, | ||
| "require-dev": { | ||
| "phpunit/phpunit": "4.*" | ||
| }, | ||
| "autoload": { | ||
| "psr-0": { "Doctrine\\Common\\Inflector\\": "lib/" } | ||
| }, | ||
| "extra": { | ||
| "branch-alias": { | ||
| "dev-master": "1.1.x-dev" | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,31 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
|
|
||
| <phpunit backupGlobals="false" | ||
| backupStaticAttributes="false" | ||
| colors="true" | ||
| convertErrorsToExceptions="true" | ||
| convertNoticesToExceptions="true" | ||
| convertWarningsToExceptions="true" | ||
| processIsolation="false" | ||
| stopOnFailure="false" | ||
| syntaxCheck="false" | ||
| bootstrap="./tests/Doctrine/Tests/TestInit.php" | ||
| > | ||
| <testsuites> | ||
| <testsuite name="Doctrine Inflector Test Suite"> | ||
| <directory>./tests/Doctrine/</directory> | ||
| </testsuite> | ||
| </testsuites> | ||
|
|
||
| <filter> | ||
| <whitelist> | ||
| <directory>./lib/Doctrine/</directory> | ||
| </whitelist> | ||
| </filter> | ||
|
|
||
| <groups> | ||
| <exclude> | ||
| <group>performance</group> | ||
| </exclude> | ||
| </groups> | ||
| </phpunit> |
| @@ -0,0 +1,309 @@ | ||
| <?php | ||
|
|
||
| namespace Doctrine\Tests\Common\Inflector; | ||
|
|
||
| use Doctrine\Tests\DoctrineTestCase; | ||
| use Doctrine\Common\Inflector\Inflector; | ||
|
|
||
| class InflectorTest extends DoctrineTestCase | ||
| { | ||
| /** | ||
| * Singular & Plural test data. Returns an array of sample words. | ||
| * | ||
| * @return array | ||
| */ | ||
| public function dataSampleWords() | ||
| { | ||
| Inflector::reset(); | ||
|
|
||
| // In the format array('singular', 'plural') | ||
| return array( | ||
| array('', ''), | ||
| array('Alias', 'Aliases'), | ||
| array('alumnus', 'alumni'), | ||
| array('analysis', 'analyses'), | ||
| array('aquarium', 'aquaria'), | ||
| array('arch', 'arches'), | ||
| array('atlas', 'atlases'), | ||
| array('axe', 'axes'), | ||
| array('baby', 'babies'), | ||
| array('bacillus', 'bacilli'), | ||
| array('bacterium', 'bacteria'), | ||
| array('bureau', 'bureaus'), | ||
| array('bus', 'buses'), | ||
| array('Bus', 'Buses'), | ||
| array('cactus', 'cacti'), | ||
| array('cafe', 'cafes'), | ||
| array('calf', 'calves'), | ||
| array('categoria', 'categorias'), | ||
| array('chateau', 'chateaux'), | ||
| array('cherry', 'cherries'), | ||
| array('child', 'children'), | ||
| array('church', 'churches'), | ||
| array('circus', 'circuses'), | ||
| array('city', 'cities'), | ||
| array('cod', 'cod'), | ||
| array('cookie', 'cookies'), | ||
| array('copy', 'copies'), | ||
| array('crisis', 'crises'), | ||
| array('criterion', 'criteria'), | ||
| array('curriculum', 'curricula'), | ||
| array('curve', 'curves'), | ||
| array('deer', 'deer'), | ||
| array('demo', 'demos'), | ||
| array('dictionary', 'dictionaries'), | ||
| array('domino', 'dominoes'), | ||
| array('dwarf', 'dwarves'), | ||
| array('echo', 'echoes'), | ||
| array('elf', 'elves'), | ||
| array('emphasis', 'emphases'), | ||
| array('family', 'families'), | ||
| array('fax', 'faxes'), | ||
| array('fish', 'fish'), | ||
| array('flush', 'flushes'), | ||
| array('fly', 'flies'), | ||
| array('focus', 'foci'), | ||
| array('foe', 'foes'), | ||
| array('food_menu', 'food_menus'), | ||
| array('FoodMenu', 'FoodMenus'), | ||
| array('foot', 'feet'), | ||
| array('fungus', 'fungi'), | ||
| array('glove', 'gloves'), | ||
| array('half', 'halves'), | ||
| array('hero', 'heroes'), | ||
| array('hippopotamus', 'hippopotami'), | ||
| array('hoax', 'hoaxes'), | ||
| array('house', 'houses'), | ||
| array('human', 'humans'), | ||
| array('identity', 'identities'), | ||
| array('index', 'indices'), | ||
| array('iris', 'irises'), | ||
| array('kiss', 'kisses'), | ||
| array('knife', 'knives'), | ||
| array('leaf', 'leaves'), | ||
| array('life', 'lives'), | ||
| array('loaf', 'loaves'), | ||
| array('man', 'men'), | ||
| array('matrix', 'matrices'), | ||
| array('matrix_row', 'matrix_rows'), | ||
| array('medium', 'media'), | ||
| array('memorandum', 'memoranda'), | ||
| array('menu', 'menus'), | ||
| array('Menu', 'Menus'), | ||
| array('mess', 'messes'), | ||
| array('moose', 'moose'), | ||
| array('motto', 'mottoes'), | ||
| array('mouse', 'mice'), | ||
| array('neurosis', 'neuroses'), | ||
| array('news', 'news'), | ||
| array('NodeMedia', 'NodeMedia'), | ||
| array('nucleus', 'nuclei'), | ||
| array('oasis', 'oases'), | ||
| array('octopus', 'octopuses'), | ||
| array('pass', 'passes'), | ||
| array('person', 'people'), | ||
| array('plateau', 'plateaux'), | ||
| array('potato', 'potatoes'), | ||
| array('powerhouse', 'powerhouses'), | ||
| array('quiz', 'quizzes'), | ||
| array('radius', 'radii'), | ||
| array('reflex', 'reflexes'), | ||
| array('roof', 'roofs'), | ||
| array('runner-up', 'runners-up'), | ||
| array('scarf', 'scarves'), | ||
| array('scratch', 'scratches'), | ||
| array('series', 'series'), | ||
| array('sheep', 'sheep'), | ||
| array('shelf', 'shelves'), | ||
| array('shoe', 'shoes'), | ||
| array('son-in-law', 'sons-in-law'), | ||
| array('species', 'species'), | ||
| array('splash', 'splashes'), | ||
| array('spy', 'spies'), | ||
| array('stimulus', 'stimuli'), | ||
| array('stitch', 'stitches'), | ||
| array('story', 'stories'), | ||
| array('syllabus', 'syllabi'), | ||
| array('tax', 'taxes'), | ||
| array('terminus', 'termini'), | ||
| array('thesis', 'theses'), | ||
| array('thief', 'thieves'), | ||
| array('tomato', 'tomatoes'), | ||
| array('tooth', 'teeth'), | ||
| array('tornado', 'tornadoes'), | ||
| array('try', 'tries'), | ||
| array('vertex', 'vertices'), | ||
| array('virus', 'viri'), | ||
| array('volcano', 'volcanoes'), | ||
| array('wash', 'washes'), | ||
| array('watch', 'watches'), | ||
| array('wave', 'waves'), | ||
| array('wharf', 'wharves'), | ||
| array('wife', 'wives'), | ||
| array('woman', 'women'), | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * testInflectingSingulars method | ||
| * | ||
| * @dataProvider dataSampleWords | ||
| * @return void | ||
| */ | ||
| public function testInflectingSingulars($singular, $plural) | ||
| { | ||
| $this->assertEquals( | ||
| $singular, | ||
| Inflector::singularize($plural), | ||
| "'$plural' should be singularized to '$singular'" | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * testInflectingPlurals method | ||
| * | ||
| * @dataProvider dataSampleWords | ||
| * @return void | ||
| */ | ||
| public function testInflectingPlurals($singular, $plural) | ||
| { | ||
| $this->assertEquals( | ||
| $plural, | ||
| Inflector::pluralize($singular), | ||
| "'$singular' should be pluralized to '$plural'" | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * testCustomPluralRule method | ||
| * | ||
| * @return void | ||
| */ | ||
| public function testCustomPluralRule() | ||
| { | ||
| Inflector::reset(); | ||
| Inflector::rules('plural', array('/^(custom)$/i' => '\1izables')); | ||
|
|
||
| $this->assertEquals(Inflector::pluralize('custom'), 'customizables'); | ||
|
|
||
| Inflector::rules('plural', array('uninflected' => array('uninflectable'))); | ||
|
|
||
| $this->assertEquals(Inflector::pluralize('uninflectable'), 'uninflectable'); | ||
|
|
||
| Inflector::rules('plural', array( | ||
| 'rules' => array('/^(alert)$/i' => '\1ables'), | ||
| 'uninflected' => array('noflect', 'abtuse'), | ||
| 'irregular' => array('amaze' => 'amazable', 'phone' => 'phonezes') | ||
| )); | ||
|
|
||
| $this->assertEquals(Inflector::pluralize('noflect'), 'noflect'); | ||
| $this->assertEquals(Inflector::pluralize('abtuse'), 'abtuse'); | ||
| $this->assertEquals(Inflector::pluralize('alert'), 'alertables'); | ||
| $this->assertEquals(Inflector::pluralize('amaze'), 'amazable'); | ||
| $this->assertEquals(Inflector::pluralize('phone'), 'phonezes'); | ||
| } | ||
|
|
||
| /** | ||
| * testCustomSingularRule method | ||
| * | ||
| * @return void | ||
| */ | ||
| public function testCustomSingularRule() | ||
| { | ||
| Inflector::reset(); | ||
| Inflector::rules('singular', array('/(eple)r$/i' => '\1', '/(jente)r$/i' => '\1')); | ||
|
|
||
| $this->assertEquals(Inflector::singularize('epler'), 'eple'); | ||
| $this->assertEquals(Inflector::singularize('jenter'), 'jente'); | ||
|
|
||
| Inflector::rules('singular', array( | ||
| 'rules' => array('/^(bil)er$/i' => '\1', '/^(inflec|contribu)tors$/i' => '\1ta'), | ||
| 'uninflected' => array('singulars'), | ||
| 'irregular' => array('spins' => 'spinor') | ||
| )); | ||
|
|
||
| $this->assertEquals(Inflector::singularize('inflectors'), 'inflecta'); | ||
| $this->assertEquals(Inflector::singularize('contributors'), 'contributa'); | ||
| $this->assertEquals(Inflector::singularize('spins'), 'spinor'); | ||
| $this->assertEquals(Inflector::singularize('singulars'), 'singulars'); | ||
| } | ||
|
|
||
| /** | ||
| * test that setting new rules clears the inflector caches. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function testRulesClearsCaches() | ||
| { | ||
| Inflector::reset(); | ||
|
|
||
| $this->assertEquals(Inflector::singularize('Bananas'), 'Banana'); | ||
| $this->assertEquals(Inflector::pluralize('Banana'), 'Bananas'); | ||
|
|
||
| Inflector::rules('singular', array( | ||
| 'rules' => array('/(.*)nas$/i' => '\1zzz') | ||
| )); | ||
|
|
||
| $this->assertEquals('Banazzz', Inflector::singularize('Bananas'), 'Was inflected with old rules.'); | ||
|
|
||
| Inflector::rules('plural', array( | ||
| 'rules' => array('/(.*)na$/i' => '\1zzz'), | ||
| 'irregular' => array('corpus' => 'corpora') | ||
| )); | ||
|
|
||
| $this->assertEquals(Inflector::pluralize('Banana'), 'Banazzz', 'Was inflected with old rules.'); | ||
| $this->assertEquals(Inflector::pluralize('corpus'), 'corpora', 'Was inflected with old irregular form.'); | ||
| } | ||
|
|
||
| /** | ||
| * Test resetting inflection rules. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function testCustomRuleWithReset() | ||
| { | ||
| Inflector::reset(); | ||
|
|
||
| $uninflected = array('atlas', 'lapis', 'onibus', 'pires', 'virus', '.*x'); | ||
| $pluralIrregular = array('as' => 'ases'); | ||
|
|
||
| Inflector::rules('singular', array( | ||
| 'rules' => array('/^(.*)(a|e|o|u)is$/i' => '\1\2l'), | ||
| 'uninflected' => $uninflected, | ||
| ), true); | ||
|
|
||
| Inflector::rules('plural', array( | ||
| 'rules' => array( | ||
| '/^(.*)(a|e|o|u)l$/i' => '\1\2is', | ||
| ), | ||
| 'uninflected' => $uninflected, | ||
| 'irregular' => $pluralIrregular | ||
| ), true); | ||
|
|
||
| $this->assertEquals(Inflector::pluralize('Alcool'), 'Alcoois'); | ||
| $this->assertEquals(Inflector::pluralize('Atlas'), 'Atlas'); | ||
| $this->assertEquals(Inflector::singularize('Alcoois'), 'Alcool'); | ||
| $this->assertEquals(Inflector::singularize('Atlas'), 'Atlas'); | ||
| } | ||
|
|
||
| /** | ||
| * Test basic ucwords functionality. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function testUcwords() | ||
| { | ||
| $this->assertSame('Top-O-The-Morning To All_of_you!', Inflector::ucwords( 'top-o-the-morning to all_of_you!')); | ||
| } | ||
|
|
||
| /** | ||
| * Test ucwords functionality with custom delimeters. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function testUcwordsWithCustomDelimeters() | ||
| { | ||
| $this->assertSame('Top-O-The-Morning To All_Of_You!', Inflector::ucwords( 'top-o-the-morning to all_of_you!', '-_ ')); | ||
| } | ||
| } | ||
|
|
| @@ -0,0 +1,10 @@ | ||
| <?php | ||
|
|
||
| namespace Doctrine\Tests; | ||
|
|
||
| /** | ||
| * Base testcase class for all Doctrine testcases. | ||
| */ | ||
| abstract class DoctrineTestCase extends \PHPUnit_Framework_TestCase | ||
| { | ||
| } |
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
| /* | ||
| * This file bootstraps the test environment. | ||
| */ | ||
| namespace Doctrine\Tests; | ||
|
|
||
| error_reporting(E_ALL | E_STRICT); | ||
|
|
||
| // register silently failing autoloader | ||
| spl_autoload_register(function($class) | ||
| { | ||
| if (0 === strpos($class, 'Doctrine\Tests\\')) { | ||
| $path = __DIR__.'/../../'.strtr($class, '\\', '/').'.php'; | ||
| if (is_file($path) && is_readable($path)) { | ||
| require_once $path; | ||
|
|
||
| return true; | ||
| } | ||
| } else if (0 === strpos($class, 'Doctrine\Common\\')) { | ||
| $path = __DIR__.'/../../../lib/'.($class = strtr($class, '\\', '/')).'.php'; | ||
| if (is_file($path) && is_readable($path)) { | ||
| require_once $path; | ||
|
|
||
| return true; | ||
| } | ||
| } | ||
| }); |
| @@ -0,0 +1,19 @@ | ||
| #The MIT License | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| THE SOFTWARE. |
| @@ -0,0 +1,33 @@ | ||
| { | ||
| "name": "filp/whoops", | ||
| "license": "MIT", | ||
| "description": "php error handling for cool kids", | ||
| "keywords": ["library", "error", "handling", "exception", "silex-provider", "whoops", "zf2"], | ||
| "homepage": "https://github.com/filp/whoops", | ||
| "authors": [ | ||
| { | ||
| "name": "Filipe Dobreira", | ||
| "homepage": "https://github.com/filp", | ||
| "role": "Developer" | ||
| } | ||
| ], | ||
| "require": { | ||
| "php": ">=5.3.0" | ||
| }, | ||
| "require-dev": { | ||
| "mockery/mockery": "0.9.*" | ||
| }, | ||
| "autoload": { | ||
| "psr-0": { | ||
| "Whoops": "src/" | ||
| }, | ||
| "classmap": [ | ||
| "src/deprecated" | ||
| ] | ||
| }, | ||
| "extra": { | ||
| "branch-alias": { | ||
| "dev-master": "1.2-dev" | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,17 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Exception; | ||
|
|
||
| use ErrorException as BaseErrorException; | ||
|
|
||
| /** | ||
| * Wraps ErrorException; mostly used for typing (at least now) | ||
| * to easily cleanup the stack trace of redundant info. | ||
| */ | ||
| class ErrorException extends BaseErrorException | ||
| { | ||
| } |
| @@ -0,0 +1,74 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Exception; | ||
|
|
||
|
|
||
| class Formatter | ||
| { | ||
| /** | ||
| * Returns all basic information about the exception in a simple array | ||
| * for further convertion to other languages | ||
| * @param Inspector $inspector | ||
| * @param bool $shouldAddTrace | ||
| * @return array | ||
| */ | ||
| public static function formatExceptionAsDataArray(Inspector $inspector, $shouldAddTrace) | ||
| { | ||
| $exception = $inspector->getException(); | ||
| $response = array( | ||
| 'type' => get_class($exception), | ||
| 'message' => $exception->getMessage(), | ||
| 'file' => $exception->getFile(), | ||
| 'line' => $exception->getLine(), | ||
| ); | ||
|
|
||
| if ($shouldAddTrace) { | ||
| $frames = $inspector->getFrames(); | ||
| $frameData = array(); | ||
|
|
||
| foreach ($frames as $frame) { | ||
| /** @var Frame $frame */ | ||
| $frameData[] = array( | ||
| 'file' => $frame->getFile(), | ||
| 'line' => $frame->getLine(), | ||
| 'function' => $frame->getFunction(), | ||
| 'class' => $frame->getClass(), | ||
| 'args' => $frame->getArgs(), | ||
| ); | ||
| } | ||
|
|
||
| $response['trace'] = $frameData; | ||
| } | ||
|
|
||
| return $response; | ||
| } | ||
|
|
||
| public static function formatExceptionPlain(Inspector $inspector) | ||
| { | ||
| $message = $inspector->getException()->getMessage(); | ||
| $frames = $inspector->getFrames(); | ||
|
|
||
| $plain = $inspector->getExceptionName(); | ||
| $plain .= ' thrown with message "'; | ||
| $plain .= $message; | ||
| $plain .= '"'."\n\n"; | ||
|
|
||
| $plain .= "Stacktrace:\n"; | ||
| foreach ($frames as $i => $frame) { | ||
| $plain .= "#". (count($frames) - $i - 1). " "; | ||
| $plain .= $frame->getClass() ?: ''; | ||
| $plain .= $frame->getClass() && $frame->getFunction() ? ":" : ""; | ||
| $plain .= $frame->getFunction() ?: ''; | ||
| $plain .= ' in '; | ||
| $plain .= ($frame->getFile() ?: '<#unknown>'); | ||
| $plain .= ':'; | ||
| $plain .= (int) $frame->getLine(). "\n"; | ||
| } | ||
|
|
||
| return $plain; | ||
| } | ||
| } |
| @@ -0,0 +1,269 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Exception; | ||
|
|
||
| use InvalidArgumentException; | ||
| use Serializable; | ||
|
|
||
| class Frame implements Serializable | ||
| { | ||
| /** | ||
| * @var array | ||
| */ | ||
| protected $frame; | ||
|
|
||
| /** | ||
| * @var string | ||
| */ | ||
| protected $fileContentsCache; | ||
|
|
||
| /** | ||
| * @var array[] | ||
| */ | ||
| protected $comments = array(); | ||
|
|
||
| /** | ||
| * @param array[] | ||
| */ | ||
| public function __construct(array $frame) | ||
| { | ||
| $this->frame = $frame; | ||
| } | ||
|
|
||
| /** | ||
| * @param bool $shortened | ||
| * @return string|null | ||
| */ | ||
| public function getFile($shortened = false) | ||
| { | ||
| if (empty($this->frame['file'])) { | ||
| return null; | ||
| } | ||
|
|
||
| $file = $this->frame['file']; | ||
|
|
||
| // Check if this frame occurred within an eval(). | ||
| // @todo: This can be made more reliable by checking if we've entered | ||
| // eval() in a previous trace, but will need some more work on the upper | ||
| // trace collector(s). | ||
| if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert) code$/', $file, $matches)) { | ||
| $file = $this->frame['file'] = $matches[1]; | ||
| $this->frame['line'] = (int) $matches[2]; | ||
| } | ||
|
|
||
| if ($shortened && is_string($file)) { | ||
| // Replace the part of the path that all frames have in common, and add 'soft hyphens' for smoother line-breaks. | ||
| $dirname = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__)))))); | ||
| $file = str_replace($dirname, "…", $file); | ||
| $file = str_replace("/", "/­", $file); | ||
| } | ||
|
|
||
| return $file; | ||
| } | ||
|
|
||
| /** | ||
| * @return int|null | ||
| */ | ||
| public function getLine() | ||
| { | ||
| return isset($this->frame['line']) ? $this->frame['line'] : null; | ||
| } | ||
|
|
||
| /** | ||
| * @return string|null | ||
| */ | ||
| public function getClass() | ||
| { | ||
| return isset($this->frame['class']) ? $this->frame['class'] : null; | ||
| } | ||
|
|
||
| /** | ||
| * @return string|null | ||
| */ | ||
| public function getFunction() | ||
| { | ||
| return isset($this->frame['function']) ? $this->frame['function'] : null; | ||
| } | ||
|
|
||
| /** | ||
| * @return array | ||
| */ | ||
| public function getArgs() | ||
| { | ||
| return isset($this->frame['args']) ? (array) $this->frame['args'] : array(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the full contents of the file for this frame, | ||
| * if it's known. | ||
| * @return string|null | ||
| */ | ||
| public function getFileContents() | ||
| { | ||
| if ($this->fileContentsCache === null && $filePath = $this->getFile()) { | ||
| // Leave the stage early when 'Unknown' is passed | ||
| // this would otherwise raise an exception when | ||
| // open_basedir is enabled. | ||
| if ($filePath === "Unknown") { | ||
| return null; | ||
| } | ||
|
|
||
| // Return null if the file doesn't actually exist. | ||
| if (!is_file($filePath)) { | ||
| return null; | ||
| } | ||
|
|
||
| $this->fileContentsCache = file_get_contents($filePath); | ||
| } | ||
|
|
||
| return $this->fileContentsCache; | ||
| } | ||
|
|
||
| /** | ||
| * Adds a comment to this frame, that can be received and | ||
| * used by other handlers. For example, the PrettyPage handler | ||
| * can attach these comments under the code for each frame. | ||
| * | ||
| * An interesting use for this would be, for example, code analysis | ||
| * & annotations. | ||
| * | ||
| * @param string $comment | ||
| * @param string $context Optional string identifying the origin of the comment | ||
| */ | ||
| public function addComment($comment, $context = 'global') | ||
| { | ||
| $this->comments[] = array( | ||
| 'comment' => $comment, | ||
| 'context' => $context, | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns all comments for this frame. Optionally allows | ||
| * a filter to only retrieve comments from a specific | ||
| * context. | ||
| * | ||
| * @param string $filter | ||
| * @return array[] | ||
| */ | ||
| public function getComments($filter = null) | ||
| { | ||
| $comments = $this->comments; | ||
|
|
||
| if ($filter !== null) { | ||
| $comments = array_filter($comments, function ($c) use ($filter) { | ||
| return $c['context'] == $filter; | ||
| }); | ||
| } | ||
|
|
||
| return $comments; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the array containing the raw frame data from which | ||
| * this Frame object was built | ||
| * | ||
| * @return array | ||
| */ | ||
| public function getRawFrame() | ||
| { | ||
| return $this->frame; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the contents of the file for this frame as an | ||
| * array of lines, and optionally as a clamped range of lines. | ||
| * | ||
| * NOTE: lines are 0-indexed | ||
| * | ||
| * @example | ||
| * Get all lines for this file | ||
| * $frame->getFileLines(); // => array( 0 => '<?php', 1 => '...', ...) | ||
| * @example | ||
| * Get one line for this file, starting at line 10 (zero-indexed, remember!) | ||
| * $frame->getFileLines(9, 1); // array( 10 => '...', 11 => '...') | ||
| * | ||
| * @throws InvalidArgumentException if $length is less than or equal to 0 | ||
| * @param int $start | ||
| * @param int $length | ||
| * @return string[]|null | ||
| */ | ||
| public function getFileLines($start = 0, $length = null) | ||
| { | ||
| if (null !== ($contents = $this->getFileContents())) { | ||
| $lines = explode("\n", $contents); | ||
|
|
||
| // Get a subset of lines from $start to $end | ||
| if ($length !== null) { | ||
| $start = (int) $start; | ||
| $length = (int) $length; | ||
| if ($start < 0) { | ||
| $start = 0; | ||
| } | ||
|
|
||
| if ($length <= 0) { | ||
| throw new InvalidArgumentException( | ||
| "\$length($length) cannot be lower or equal to 0" | ||
| ); | ||
| } | ||
|
|
||
| $lines = array_slice($lines, $start, $length, true); | ||
| } | ||
|
|
||
| return $lines; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Implements the Serializable interface, with special | ||
| * steps to also save the existing comments. | ||
| * | ||
| * @see Serializable::serialize | ||
| * @return string | ||
| */ | ||
| public function serialize() | ||
| { | ||
| $frame = $this->frame; | ||
| if (!empty($this->comments)) { | ||
| $frame['_comments'] = $this->comments; | ||
| } | ||
|
|
||
| return serialize($frame); | ||
| } | ||
|
|
||
| /** | ||
| * Unserializes the frame data, while also preserving | ||
| * any existing comment data. | ||
| * | ||
| * @see Serializable::unserialize | ||
| * @param string $serializedFrame | ||
| */ | ||
| public function unserialize($serializedFrame) | ||
| { | ||
| $frame = unserialize($serializedFrame); | ||
|
|
||
| if (!empty($frame['_comments'])) { | ||
| $this->comments = $frame['_comments']; | ||
| unset($frame['_comments']); | ||
| } | ||
|
|
||
| $this->frame = $frame; | ||
| } | ||
|
|
||
| /** | ||
| * Compares Frame against one another | ||
| * @param Frame $frame | ||
| * @return bool | ||
| */ | ||
| public function equals(Frame $frame) | ||
| { | ||
| if (!$this->getFile() || $this->getFile() === 'Unknown' || !$this->getLine()) { | ||
| return false; | ||
| } | ||
| return $frame->getFile() === $this->getFile() && $frame->getLine() === $this->getLine(); | ||
| } | ||
| } |
| @@ -0,0 +1,191 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Exception; | ||
|
|
||
| use ArrayAccess; | ||
| use ArrayIterator; | ||
| use Countable; | ||
| use IteratorAggregate; | ||
| use Serializable; | ||
| use UnexpectedValueException; | ||
|
|
||
| /** | ||
| * Exposes a fluent interface for dealing with an ordered list | ||
| * of stack-trace frames. | ||
| */ | ||
| class FrameCollection implements ArrayAccess, IteratorAggregate, Serializable, Countable | ||
| { | ||
| /** | ||
| * @var array[] | ||
| */ | ||
| private $frames; | ||
|
|
||
| /** | ||
| * @param array $frames | ||
| */ | ||
| public function __construct(array $frames) | ||
| { | ||
| $this->frames = array_map(function ($frame) { | ||
| return new Frame($frame); | ||
| }, $frames); | ||
| } | ||
|
|
||
| /** | ||
| * Filters frames using a callable, returns the same FrameCollection | ||
| * | ||
| * @param callable $callable | ||
| * @return FrameCollection | ||
| */ | ||
| public function filter($callable) | ||
| { | ||
| $this->frames = array_filter($this->frames, $callable); | ||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * Map the collection of frames | ||
| * | ||
| * @param callable $callable | ||
| * @return FrameCollection | ||
| */ | ||
| public function map($callable) | ||
| { | ||
| // Contain the map within a higher-order callable | ||
| // that enforces type-correctness for the $callable | ||
| $this->frames = array_map(function ($frame) use ($callable) { | ||
| $frame = call_user_func($callable, $frame); | ||
|
|
||
| if (!$frame instanceof Frame) { | ||
| throw new UnexpectedValueException( | ||
| "Callable to " . __METHOD__ . " must return a Frame object" | ||
| ); | ||
| } | ||
|
|
||
| return $frame; | ||
| }, $this->frames); | ||
|
|
||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * Returns an array with all frames, does not affect | ||
| * the internal array. | ||
| * | ||
| * @todo If this gets any more complex than this, | ||
| * have getIterator use this method. | ||
| * @see FrameCollection::getIterator | ||
| * @return array | ||
| */ | ||
| public function getArray() | ||
| { | ||
| return $this->frames; | ||
| } | ||
|
|
||
| /** | ||
| * @see IteratorAggregate::getIterator | ||
| * @return ArrayIterator | ||
| */ | ||
| public function getIterator() | ||
| { | ||
| return new ArrayIterator($this->frames); | ||
| } | ||
|
|
||
| /** | ||
| * @see ArrayAccess::offsetExists | ||
| * @param int $offset | ||
| */ | ||
| public function offsetExists($offset) | ||
| { | ||
| return isset($this->frames[$offset]); | ||
| } | ||
|
|
||
| /** | ||
| * @see ArrayAccess::offsetGet | ||
| * @param int $offset | ||
| */ | ||
| public function offsetGet($offset) | ||
| { | ||
| return $this->frames[$offset]; | ||
| } | ||
|
|
||
| /** | ||
| * @see ArrayAccess::offsetSet | ||
| * @param int $offset | ||
| */ | ||
| public function offsetSet($offset, $value) | ||
| { | ||
| throw new \Exception(__CLASS__ . ' is read only'); | ||
| } | ||
|
|
||
| /** | ||
| * @see ArrayAccess::offsetUnset | ||
| * @param int $offset | ||
| */ | ||
| public function offsetUnset($offset) | ||
| { | ||
| throw new \Exception(__CLASS__ . ' is read only'); | ||
| } | ||
|
|
||
| /** | ||
| * @see Countable::count | ||
| * @return int | ||
| */ | ||
| public function count() | ||
| { | ||
| return count($this->frames); | ||
| } | ||
|
|
||
| /** | ||
| * @see Serializable::serialize | ||
| * @return string | ||
| */ | ||
| public function serialize() | ||
| { | ||
| return serialize($this->frames); | ||
| } | ||
|
|
||
| /** | ||
| * @see Serializable::unserialize | ||
| * @param string $serializedFrames | ||
| */ | ||
| public function unserialize($serializedFrames) | ||
| { | ||
| $this->frames = unserialize($serializedFrames); | ||
| } | ||
|
|
||
| /** | ||
| * @param Frame[] $frames Array of Frame instances, usually from $e->getPrevious() | ||
| */ | ||
| public function prependFrames(array $frames) | ||
| { | ||
| $this->frames = array_merge($frames, $this->frames); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the innermost part of stack trace that is not the same as that of outer exception | ||
| * | ||
| * @param FrameCollection $parentFrames Outer exception frames to compare tail against | ||
| * @return Frame[] | ||
| */ | ||
| public function topDiff(FrameCollection $parentFrames) | ||
| { | ||
| $diff = $this->frames; | ||
|
|
||
| $parentFrames = $parentFrames->getArray(); | ||
| $p = count($parentFrames)-1; | ||
|
|
||
| for ($i = count($diff)-1; $i >= 0 && $p >= 0; $i--) { | ||
| /** @var Frame $tailFrame */ | ||
| $tailFrame = $diff[$i]; | ||
| if ($tailFrame->equals($parentFrames[$p])) { | ||
| unset($diff[$i]); | ||
| } | ||
| $p--; | ||
| } | ||
| return $diff; | ||
| } | ||
| } |
| @@ -0,0 +1,161 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Exception; | ||
|
|
||
| use Exception; | ||
|
|
||
| class Inspector | ||
| { | ||
| /** | ||
| * @var Exception | ||
| */ | ||
| private $exception; | ||
|
|
||
| /** | ||
| * @var \Whoops\Exception\FrameCollection | ||
| */ | ||
| private $frames; | ||
|
|
||
| /** | ||
| * @var \Whoops\Exception\Inspector | ||
| */ | ||
| private $previousExceptionInspector; | ||
|
|
||
| /** | ||
| * @param Exception $exception The exception to inspect | ||
| */ | ||
| public function __construct(Exception $exception) | ||
| { | ||
| $this->exception = $exception; | ||
| } | ||
|
|
||
| /** | ||
| * @return Exception | ||
| */ | ||
| public function getException() | ||
| { | ||
| return $this->exception; | ||
| } | ||
|
|
||
| /** | ||
| * @return string | ||
| */ | ||
| public function getExceptionName() | ||
| { | ||
| return get_class($this->exception); | ||
| } | ||
|
|
||
| /** | ||
| * @return string | ||
| */ | ||
| public function getExceptionMessage() | ||
| { | ||
| return $this->exception->getMessage(); | ||
| } | ||
|
|
||
| /** | ||
| * Does the wrapped Exception has a previous Exception? | ||
| * @return bool | ||
| */ | ||
| public function hasPreviousException() | ||
| { | ||
| return $this->previousExceptionInspector || $this->exception->getPrevious(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns an Inspector for a previous Exception, if any. | ||
| * @todo Clean this up a bit, cache stuff a bit better. | ||
| * @return Inspector | ||
| */ | ||
| public function getPreviousExceptionInspector() | ||
| { | ||
| if ($this->previousExceptionInspector === null) { | ||
| $previousException = $this->exception->getPrevious(); | ||
|
|
||
| if ($previousException) { | ||
| $this->previousExceptionInspector = new Inspector($previousException); | ||
| } | ||
| } | ||
|
|
||
| return $this->previousExceptionInspector; | ||
| } | ||
|
|
||
| /** | ||
| * Returns an iterator for the inspected exception's | ||
| * frames. | ||
| * @return \Whoops\Exception\FrameCollection | ||
| */ | ||
| public function getFrames() | ||
| { | ||
| if ($this->frames === null) { | ||
| $frames = $this->exception->getTrace(); | ||
|
|
||
| // If we're handling an ErrorException thrown by Whoops, | ||
| // get rid of the last frame, which matches the handleError method, | ||
| // and do not add the current exception to trace. We ensure that | ||
| // the next frame does have a filename / linenumber, though. | ||
| if ($this->exception instanceof ErrorException && empty($frames[1]['line'])) { | ||
| $frames = array($this->getFrameFromError($this->exception)); | ||
| } else { | ||
| $firstFrame = $this->getFrameFromException($this->exception); | ||
| array_unshift($frames, $firstFrame); | ||
| } | ||
| $this->frames = new FrameCollection($frames); | ||
|
|
||
| if ($previousInspector = $this->getPreviousExceptionInspector()) { | ||
| // Keep outer frame on top of the inner one | ||
| $outerFrames = $this->frames; | ||
| $newFrames = clone $previousInspector->getFrames(); | ||
| // I assume it will always be set, but let's be safe | ||
| if (isset($newFrames[0])) { | ||
| $newFrames[0]->addComment( | ||
| $previousInspector->getExceptionMessage(), | ||
| 'Exception message:' | ||
| ); | ||
| } | ||
| $newFrames->prependFrames($outerFrames->topDiff($newFrames)); | ||
| $this->frames = $newFrames; | ||
| } | ||
| } | ||
|
|
||
| return $this->frames; | ||
| } | ||
|
|
||
| /** | ||
| * Given an exception, generates an array in the format | ||
| * generated by Exception::getTrace() | ||
| * @param Exception $exception | ||
| * @return array | ||
| */ | ||
| protected function getFrameFromException(Exception $exception) | ||
| { | ||
| return array( | ||
| 'file' => $exception->getFile(), | ||
| 'line' => $exception->getLine(), | ||
| 'class' => get_class($exception), | ||
| 'args' => array( | ||
| $exception->getMessage(), | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Given an error, generates an array in the format | ||
| * generated by ErrorException | ||
| * @param ErrorException $exception | ||
| * @return array | ||
| */ | ||
| protected function getFrameFromError(ErrorException $exception) | ||
| { | ||
| return array( | ||
| 'file' => $exception->getFile(), | ||
| 'line' => $exception->getLine(), | ||
| 'class' => null, | ||
| 'args' => array(), | ||
| ); | ||
| } | ||
| } |
| @@ -0,0 +1,52 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
| use InvalidArgumentException; | ||
|
|
||
| /** | ||
| * Wrapper for Closures passed as handlers. Can be used | ||
| * directly, or will be instantiated automagically by Whoops\Run | ||
| * if passed to Run::pushHandler | ||
| */ | ||
| class CallbackHandler extends Handler | ||
| { | ||
| /** | ||
| * @var callable | ||
| */ | ||
| protected $callable; | ||
|
|
||
| /** | ||
| * @throws InvalidArgumentException If argument is not callable | ||
| * @param callable $callable | ||
| */ | ||
| public function __construct($callable) | ||
| { | ||
| if (!is_callable($callable)) { | ||
| throw new InvalidArgumentException( | ||
| 'Argument to ' . __METHOD__ . ' must be valid callable' | ||
| ); | ||
| } | ||
|
|
||
| $this->callable = $callable; | ||
| } | ||
|
|
||
| /** | ||
| * @return int|null | ||
| */ | ||
| public function handle() | ||
| { | ||
| $exception = $this->getException(); | ||
| $inspector = $this->getInspector(); | ||
| $run = $this->getRun(); | ||
| $callable = $this->callable; | ||
|
|
||
| // invoke the callable directly, to get simpler stacktraces (in comparison to call_user_func). | ||
| // this assumes that $callable is a properly typed php-callable, which we check in __construct(). | ||
| return $callable($exception, $inspector, $run); | ||
| } | ||
| } |
| @@ -0,0 +1,89 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
| use Exception; | ||
| use Whoops\Exception\Inspector; | ||
| use Whoops\Run; | ||
|
|
||
| /** | ||
| * Abstract implementation of a Handler. | ||
| */ | ||
| abstract class Handler implements HandlerInterface | ||
| { | ||
| /** | ||
| * Return constants that can be returned from Handler::handle | ||
| * to message the handler walker. | ||
| */ | ||
| const DONE = 0x10; // returning this is optional, only exists for | ||
| // semantic purposes | ||
| const LAST_HANDLER = 0x20; | ||
| const QUIT = 0x30; | ||
|
|
||
| /** | ||
| * @var Run | ||
| */ | ||
| private $run; | ||
|
|
||
| /** | ||
| * @var Inspector $inspector | ||
| */ | ||
| private $inspector; | ||
|
|
||
| /** | ||
| * @var Exception $exception | ||
| */ | ||
| private $exception; | ||
|
|
||
| /** | ||
| * @param Run $run | ||
| */ | ||
| public function setRun(Run $run) | ||
| { | ||
| $this->run = $run; | ||
| } | ||
|
|
||
| /** | ||
| * @return Run | ||
| */ | ||
| protected function getRun() | ||
| { | ||
| return $this->run; | ||
| } | ||
|
|
||
| /** | ||
| * @param Inspector $inspector | ||
| */ | ||
| public function setInspector(Inspector $inspector) | ||
| { | ||
| $this->inspector = $inspector; | ||
| } | ||
|
|
||
| /** | ||
| * @return Inspector | ||
| */ | ||
| protected function getInspector() | ||
| { | ||
| return $this->inspector; | ||
| } | ||
|
|
||
| /** | ||
| * @param Exception $exception | ||
| */ | ||
| public function setException(Exception $exception) | ||
| { | ||
| $this->exception = $exception; | ||
| } | ||
|
|
||
| /** | ||
| * @return Exception | ||
| */ | ||
| protected function getException() | ||
| { | ||
| return $this->exception; | ||
| } | ||
| } |
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
| use Exception; | ||
| use Whoops\Exception\Inspector; | ||
| use Whoops\Run; | ||
|
|
||
| interface HandlerInterface | ||
| { | ||
| /** | ||
| * @return int|null A handler may return nothing, or a Handler::HANDLE_* constant | ||
| */ | ||
| public function handle(); | ||
|
|
||
| /** | ||
| * @param Run $run | ||
| * @return void | ||
| */ | ||
| public function setRun(Run $run); | ||
|
|
||
| /** | ||
| * @param Exception $exception | ||
| * @return void | ||
| */ | ||
| public function setException(Exception $exception); | ||
|
|
||
| /** | ||
| * @param Inspector $inspector | ||
| * @return void | ||
| */ | ||
| public function setInspector(Inspector $inspector); | ||
| } |
| @@ -0,0 +1,90 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
| use Whoops\Exception\Formatter; | ||
|
|
||
| /** | ||
| * Catches an exception and converts it to a JSON | ||
| * response. Additionally can also return exception | ||
| * frames for consumption by an API. | ||
| */ | ||
| class JsonResponseHandler extends Handler | ||
| { | ||
| /** | ||
| * @var bool | ||
| */ | ||
| private $returnFrames = false; | ||
|
|
||
| /** | ||
| * @var bool | ||
| */ | ||
| private $onlyForAjaxRequests = false; | ||
|
|
||
| /** | ||
| * @param bool|null $returnFrames | ||
| * @return bool|$this | ||
| */ | ||
| public function addTraceToOutput($returnFrames = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->returnFrames; | ||
| } | ||
|
|
||
| $this->returnFrames = (bool) $returnFrames; | ||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * @param bool|null $onlyForAjaxRequests | ||
| * @return null|bool | ||
| */ | ||
| public function onlyForAjaxRequests($onlyForAjaxRequests = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->onlyForAjaxRequests; | ||
| } | ||
|
|
||
| $this->onlyForAjaxRequests = (bool) $onlyForAjaxRequests; | ||
| } | ||
|
|
||
| /** | ||
| * Check, if possible, that this execution was triggered by an AJAX request. | ||
| * | ||
| * @return bool | ||
| */ | ||
| private function isAjaxRequest() | ||
| { | ||
| return ( | ||
| !empty($_SERVER['HTTP_X_REQUESTED_WITH']) | ||
| && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); | ||
| } | ||
|
|
||
| /** | ||
| * @return int | ||
| */ | ||
| public function handle() | ||
| { | ||
| if ($this->onlyForAjaxRequests() && !$this->isAjaxRequest()) { | ||
| return Handler::DONE; | ||
| } | ||
|
|
||
| $response = array( | ||
| 'error' => Formatter::formatExceptionAsDataArray( | ||
| $this->getInspector(), | ||
| $this->addTraceToOutput() | ||
| ), | ||
| ); | ||
|
|
||
| if (\Whoops\Util\Misc::canSendHeaders()) { | ||
| header('Content-Type: application/json'); | ||
| } | ||
|
|
||
| echo json_encode($response); | ||
| return Handler::QUIT; | ||
| } | ||
| } |
| @@ -0,0 +1,331 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| * Plaintext handler for command line and logs. | ||
| * @author Pierre-Yves Landuré <https://howto.biapy.com/> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
| use InvalidArgumentException; | ||
| use Psr\Log\LoggerInterface; | ||
| use Whoops\Exception\Frame; | ||
|
|
||
| /** | ||
| * Handler outputing plaintext error messages. Can be used | ||
| * directly, or will be instantiated automagically by Whoops\Run | ||
| * if passed to Run::pushHandler | ||
| */ | ||
| class PlainTextHandler extends Handler | ||
| { | ||
| const VAR_DUMP_PREFIX = ' | '; | ||
|
|
||
| /** | ||
| * @var \Psr\Log\LoggerInterface | ||
| */ | ||
| protected $logger; | ||
|
|
||
| /** | ||
| * @var bool | ||
| */ | ||
| private $addTraceToOutput = true; | ||
|
|
||
| /** | ||
| * @var bool|integer | ||
| */ | ||
| private $addTraceFunctionArgsToOutput = false; | ||
|
|
||
| /** | ||
| * @var integer | ||
| */ | ||
| private $traceFunctionArgsOutputLimit = 1024; | ||
|
|
||
| /** | ||
| * @var bool | ||
| */ | ||
| private $onlyForCommandLine = false; | ||
|
|
||
| /** | ||
| * @var bool | ||
| */ | ||
| private $outputOnlyIfCommandLine = true; | ||
|
|
||
| /** | ||
| * @var bool | ||
| */ | ||
| private $loggerOnly = false; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * @throws InvalidArgumentException If argument is not null or a LoggerInterface | ||
| * @param \Psr\Log\LoggerInterface|null $logger | ||
| */ | ||
| public function __construct($logger = null) | ||
| { | ||
| $this->setLogger($logger); | ||
| } | ||
|
|
||
| /** | ||
| * Set the output logger interface. | ||
| * @throws InvalidArgumentException If argument is not null or a LoggerInterface | ||
| * @param \Psr\Log\LoggerInterface|null $logger | ||
| */ | ||
| public function setLogger($logger = null) | ||
| { | ||
| if (! (is_null($logger) | ||
| || $logger instanceof LoggerInterface)) { | ||
| throw new InvalidArgumentException( | ||
| 'Argument to ' . __METHOD__ . | ||
| " must be a valid Logger Interface (aka. Monolog), " . | ||
| get_class($logger) . ' given.' | ||
| ); | ||
| } | ||
|
|
||
| $this->logger = $logger; | ||
| } | ||
|
|
||
| /** | ||
| * @return \Psr\Log\LoggerInterface|null | ||
| */ | ||
| public function getLogger() | ||
| { | ||
| return $this->logger; | ||
| } | ||
|
|
||
| /** | ||
| * Add error trace to output. | ||
| * @param bool|null $addTraceToOutput | ||
| * @return bool|$this | ||
| */ | ||
| public function addTraceToOutput($addTraceToOutput = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->addTraceToOutput; | ||
| } | ||
|
|
||
| $this->addTraceToOutput = (bool) $addTraceToOutput; | ||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * Add error trace function arguments to output. | ||
| * Set to True for all frame args, or integer for the n first frame args. | ||
| * @param bool|integer|null $addTraceFunctionArgsToOutput | ||
| * @return null|bool|integer | ||
| */ | ||
| public function addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->addTraceFunctionArgsToOutput; | ||
| } | ||
|
|
||
| if (! is_integer($addTraceFunctionArgsToOutput)) { | ||
| $this->addTraceFunctionArgsToOutput = (bool) $addTraceFunctionArgsToOutput; | ||
| } else { | ||
| $this->addTraceFunctionArgsToOutput = $addTraceFunctionArgsToOutput; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Set the size limit in bytes of frame arguments var_dump output. | ||
| * If the limit is reached, the var_dump output is discarded. | ||
| * Prevent memory limit errors. | ||
| * @var integer | ||
| */ | ||
| public function setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit) | ||
| { | ||
| $this->traceFunctionArgsOutputLimit = (integer) $traceFunctionArgsOutputLimit; | ||
| } | ||
|
|
||
| /** | ||
| * Get the size limit in bytes of frame arguments var_dump output. | ||
| * If the limit is reached, the var_dump output is discarded. | ||
| * Prevent memory limit errors. | ||
| * @return integer | ||
| */ | ||
| public function getTraceFunctionArgsOutputLimit() | ||
| { | ||
| return $this->traceFunctionArgsOutputLimit; | ||
| } | ||
|
|
||
| /** | ||
| * Restrict error handling to command line calls. | ||
| * @param bool|null $onlyForCommandLine | ||
| * @return null|bool | ||
| */ | ||
| public function onlyForCommandLine($onlyForCommandLine = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->onlyForCommandLine; | ||
| } | ||
| $this->onlyForCommandLine = (bool) $onlyForCommandLine; | ||
| } | ||
|
|
||
| /** | ||
| * Output the error message only if using command line. | ||
| * else, output to logger if available. | ||
| * Allow to safely add this handler to web pages. | ||
| * @param bool|null $outputOnlyIfCommandLine | ||
| * @return null|bool | ||
| */ | ||
| public function outputOnlyIfCommandLine($outputOnlyIfCommandLine = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->outputOnlyIfCommandLine; | ||
| } | ||
| $this->outputOnlyIfCommandLine = (bool) $outputOnlyIfCommandLine; | ||
| } | ||
|
|
||
| /** | ||
| * Only output to logger. | ||
| * @param bool|null $loggerOnly | ||
| * @return null|bool | ||
| */ | ||
| public function loggerOnly($loggerOnly = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->loggerOnly; | ||
| } | ||
|
|
||
| $this->loggerOnly = (bool) $loggerOnly; | ||
| } | ||
|
|
||
| /** | ||
| * Check, if possible, that this execution was triggered by a command line. | ||
| * @return bool | ||
| */ | ||
| private function isCommandLine() | ||
| { | ||
| return PHP_SAPI == 'cli'; | ||
| } | ||
|
|
||
| /** | ||
| * Test if handler can process the exception.. | ||
| * @return bool | ||
| */ | ||
| private function canProcess() | ||
| { | ||
| return $this->isCommandLine() || !$this->onlyForCommandLine(); | ||
| } | ||
|
|
||
| /** | ||
| * Test if handler can output to stdout. | ||
| * @return bool | ||
| */ | ||
| private function canOutput() | ||
| { | ||
| return ($this->isCommandLine() || ! $this->outputOnlyIfCommandLine()) | ||
| && ! $this->loggerOnly(); | ||
| } | ||
|
|
||
| /** | ||
| * Get the frame args var_dump. | ||
| * @param \Whoops\Exception\Frame $frame [description] | ||
| * @param integer $line [description] | ||
| * @return string | ||
| */ | ||
| private function getFrameArgsOutput(Frame $frame, $line) | ||
| { | ||
| if ($this->addTraceFunctionArgsToOutput() === false | ||
| || $this->addTraceFunctionArgsToOutput() < $line) { | ||
| return ''; | ||
| } | ||
|
|
||
| // Dump the arguments: | ||
| ob_start(); | ||
| var_dump($frame->getArgs()); | ||
| if (ob_get_length() > $this->getTraceFunctionArgsOutputLimit()) { | ||
| // The argument var_dump is to big. | ||
| // Discarded to limit memory usage. | ||
| ob_clean(); | ||
| return sprintf( | ||
| "\n%sArguments dump length greater than %d Bytes. Discarded.", | ||
| self::VAR_DUMP_PREFIX, | ||
| $this->getTraceFunctionArgsOutputLimit() | ||
| ); | ||
| } | ||
|
|
||
| return sprintf("\n%s", | ||
| preg_replace('/^/m', self::VAR_DUMP_PREFIX, ob_get_clean()) | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Get the exception trace as plain text. | ||
| * @return string | ||
| */ | ||
| private function getTraceOutput() | ||
| { | ||
| if (! $this->addTraceToOutput()) { | ||
| return ''; | ||
| } | ||
| $inspector = $this->getInspector(); | ||
| $frames = $inspector->getFrames(); | ||
|
|
||
| $response = "\nStack trace:"; | ||
|
|
||
| $line = 1; | ||
| foreach ($frames as $frame) { | ||
| /** @var Frame $frame */ | ||
| $class = $frame->getClass(); | ||
|
|
||
| $template = "\n%3d. %s->%s() %s:%d%s"; | ||
| if (! $class) { | ||
| // Remove method arrow (->) from output. | ||
| $template = "\n%3d. %s%s() %s:%d%s"; | ||
| } | ||
|
|
||
| $response .= sprintf( | ||
| $template, | ||
| $line, | ||
| $class, | ||
| $frame->getFunction(), | ||
| $frame->getFile(), | ||
| $frame->getLine(), | ||
| $this->getFrameArgsOutput($frame, $line) | ||
| ); | ||
|
|
||
| $line++; | ||
| } | ||
|
|
||
| return $response; | ||
| } | ||
|
|
||
| /** | ||
| * @return int | ||
| */ | ||
| public function handle() | ||
| { | ||
| if (! $this->canProcess()) { | ||
| return Handler::DONE; | ||
| } | ||
|
|
||
| $exception = $this->getException(); | ||
|
|
||
| $response = sprintf("%s: %s in file %s on line %d%s\n", | ||
| get_class($exception), | ||
| $exception->getMessage(), | ||
| $exception->getFile(), | ||
| $exception->getLine(), | ||
| $this->getTraceOutput() | ||
| ); | ||
|
|
||
| if ($this->getLogger()) { | ||
| $this->getLogger()->error($response); | ||
| } | ||
|
|
||
| if (! $this->canOutput()) { | ||
| return Handler::DONE; | ||
| } | ||
|
|
||
| if (class_exists('\Whoops\Util\Misc') | ||
| && \Whoops\Util\Misc::canSendHeaders()) { | ||
| header('Content-Type: text/plain'); | ||
| } | ||
|
|
||
| echo $response; | ||
|
|
||
| return Handler::QUIT; | ||
| } | ||
| } |
| @@ -0,0 +1,49 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
|
|
||
| /** | ||
| * Catches an exception and converts it to an Soap XML | ||
| * response. | ||
| * | ||
| * @author Markus Staab <http://github.com/staabm> | ||
| */ | ||
| class SoapResponseHandler extends Handler | ||
| { | ||
| /** | ||
| * @return int | ||
| */ | ||
| public function handle() | ||
| { | ||
| $exception = $this->getException(); | ||
|
|
||
| echo $this->toXml($exception); | ||
|
|
||
| return Handler::QUIT; | ||
| } | ||
|
|
||
| /** | ||
| * Converts a Exception into a SoapFault XML | ||
| */ | ||
| private function toXml(\Exception $exception) | ||
| { | ||
| $xml = ''; | ||
| $xml .= '<?xml version="1.0" encoding="UTF-8"?>'; | ||
| $xml .= '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">'; | ||
| $xml .= ' <SOAP-ENV:Body>'; | ||
| $xml .= ' <SOAP-ENV:Fault>'; | ||
| $xml .= ' <faultcode>'. htmlspecialchars($exception->getCode()) .'</faultcode>'; | ||
| $xml .= ' <faultstring>'. htmlspecialchars($exception->getMessage()) .'</faultstring>'; | ||
| $xml .= ' <detail><trace>'. htmlspecialchars($exception->getTraceAsString()) .'</trace></detail>'; | ||
| $xml .= ' </SOAP-ENV:Fault>'; | ||
| $xml .= ' </SOAP-ENV:Body>'; | ||
| $xml .= '</SOAP-ENV:Envelope>'; | ||
|
|
||
| return $xml; | ||
| } | ||
| } |
| @@ -0,0 +1,99 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Handler; | ||
|
|
||
| use SimpleXMLElement; | ||
| use Whoops\Exception\Formatter; | ||
|
|
||
| /** | ||
| * Catches an exception and converts it to an XML | ||
| * response. Additionally can also return exception | ||
| * frames for consumption by an API. | ||
| */ | ||
| class XmlResponseHandler extends Handler | ||
| { | ||
| /** | ||
| * @var bool | ||
| */ | ||
| private $returnFrames = false; | ||
|
|
||
| /** | ||
| * @param bool|null $returnFrames | ||
| * @return bool|$this | ||
| */ | ||
| public function addTraceToOutput($returnFrames = null) | ||
| { | ||
| if (func_num_args() == 0) { | ||
| return $this->returnFrames; | ||
| } | ||
|
|
||
| $this->returnFrames = (bool) $returnFrames; | ||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * @return int | ||
| */ | ||
| public function handle() | ||
| { | ||
| $response = array( | ||
| 'error' => Formatter::formatExceptionAsDataArray( | ||
| $this->getInspector(), | ||
| $this->addTraceToOutput() | ||
| ), | ||
| ); | ||
|
|
||
| echo $this->toXml($response); | ||
|
|
||
| return Handler::QUIT; | ||
| } | ||
|
|
||
| /** | ||
| * @param SimpleXMLElement $node Node to append data to, will be modified in place | ||
| * @param array|Traversable $data | ||
| * @return SimpleXMLElement The modified node, for chaining | ||
| */ | ||
| private static function addDataToNode(\SimpleXMLElement $node, $data) | ||
| { | ||
| assert('is_array($data) || $node instanceof Traversable'); | ||
|
|
||
| foreach ($data as $key => $value) { | ||
| if (is_numeric($key)) { | ||
| // Convert the key to a valid string | ||
| $key = "unknownNode_". (string) $key; | ||
| } | ||
|
|
||
| // Delete any char not allowed in XML element names | ||
| $key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key); | ||
|
|
||
| if (is_array($value)) { | ||
| $child = $node->addChild($key); | ||
| self::addDataToNode($child, $value); | ||
| } else { | ||
| $value = str_replace('&', '&', print_r($value, true)); | ||
| $node->addChild($key, $value); | ||
| } | ||
| } | ||
|
|
||
| return $node; | ||
| } | ||
|
|
||
| /** | ||
| * The main function for converting to an XML document. | ||
| * | ||
| * @param array|Traversable $data | ||
| * @return string XML | ||
| */ | ||
| private static function toXml($data) | ||
| { | ||
| assert('is_array($data) || $node instanceof Traversable'); | ||
|
|
||
| $node = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><root />"); | ||
|
|
||
| return self::addDataToNode($node, $data)->asXML(); | ||
| } | ||
| } |
| @@ -0,0 +1,78 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Provider\Phalcon; | ||
|
|
||
| use Phalcon\DI; | ||
| use Phalcon\DI\Exception; | ||
| use Whoops\Handler\JsonResponseHandler; | ||
| use Whoops\Handler\PrettyPageHandler; | ||
| use Whoops\Run; | ||
|
|
||
| class WhoopsServiceProvider | ||
| { | ||
| /** | ||
| * @param DI $di | ||
| */ | ||
| public function __construct(DI $di = null) | ||
| { | ||
| if (!$di) { | ||
| $di = DI::getDefault(); | ||
| } | ||
|
|
||
| // There's only ever going to be one error page...right? | ||
| $di->setShared('whoops.pretty_page_handler', function () { | ||
| return new PrettyPageHandler(); | ||
| }); | ||
|
|
||
| // There's only ever going to be one error page...right? | ||
| $di->setShared('whoops.json_response_handler', function () { | ||
| $jsonHandler = new JsonResponseHandler(); | ||
| $jsonHandler->onlyForAjaxRequests(true); | ||
| return $jsonHandler; | ||
| }); | ||
|
|
||
| // Retrieves info on the Phalcon environment and ships it off | ||
| // to the PrettyPageHandler's data tables: | ||
| // This works by adding a new handler to the stack that runs | ||
| // before the error page, retrieving the shared page handler | ||
| // instance, and working with it to add new data tables | ||
| $phalcon_info_handler = function () use ($di) { | ||
| try { | ||
| $request = $di['request']; | ||
| } catch (Exception $e) { | ||
| // This error occurred too early in the application's life | ||
| // and the request instance is not yet available. | ||
| return; | ||
| } | ||
|
|
||
| // Request info: | ||
| $di['whoops.pretty_page_handler']->addDataTable('Phalcon Application (Request)', array( | ||
| 'URI' => $request->getScheme().'://'.$request->getServer('HTTP_HOST').$request->getServer('REQUEST_URI'), | ||
| 'Request URI' => $request->getServer('REQUEST_URI'), | ||
| 'Path Info' => $request->getServer('PATH_INFO'), | ||
| 'Query String' => $request->getServer('QUERY_STRING') ?: '<none>', | ||
| 'HTTP Method' => $request->getMethod(), | ||
| 'Script Name' => $request->getServer('SCRIPT_NAME'), | ||
| //'Base Path' => $request->getBasePath(), | ||
| //'Base URL' => $request->getBaseUrl(), | ||
| 'Scheme' => $request->getScheme(), | ||
| 'Port' => $request->getServer('SERVER_PORT'), | ||
| 'Host' => $request->getServerName(), | ||
| )); | ||
| }; | ||
|
|
||
| $di->setShared('whoops', function () use ($di,$phalcon_info_handler) { | ||
| $run = new Run(); | ||
| $run->pushHandler($di['whoops.pretty_page_handler']); | ||
| $run->pushHandler($phalcon_info_handler); | ||
| $run->pushHandler($di['whoops.json_response_handler']); | ||
| return $run; | ||
| }); | ||
|
|
||
| $di['whoops']->register(); | ||
| } | ||
| } |
| @@ -0,0 +1,111 @@ | ||
| <?php | ||
| /** | ||
| * Whoops - php errors for cool kids | ||
| * @author Filipe Dobreira <http://github.com/filp> | ||
| */ | ||
|
|
||
| namespace Whoops\Provider\Silex; | ||
|
|
||
| use RuntimeException; | ||
| use Silex\Application; | ||
| use Silex\ServiceProviderInterface; | ||
| use Symfony\Component\HttpFoundation\Request; | ||
| use Symfony\Component\HttpFoundation\Response; | ||
| use Symfony\Component\HttpKernel\Exception\HttpException; | ||
| use Whoops\Handler\Handler; | ||
| use Whoops\Handler\PlainTextHandler; | ||
| use Whoops\Handler\PrettyPageHandler; | ||
| use Whoops\Run; | ||
|
|
||
| class WhoopsServiceProvider implements ServiceProviderInterface | ||
| { | ||
| /** | ||
| * @param Application $app | ||
| */ | ||
| public function register(Application $app) | ||
| { | ||
| // There's only ever going to be one error page...right? | ||
| $app['whoops.error_page_handler'] = $app->share(function () { | ||
| if (PHP_SAPI === 'cli') { | ||
| return new PlainTextHandler(); | ||
| } else { | ||
| return new PrettyPageHandler(); | ||
| } | ||
| }); | ||
|
|
||
| // Retrieves info on the Silex environment and ships it off | ||
| // to the PrettyPageHandler's data tables: | ||
| // This works by adding a new handler to the stack that runs | ||
| // before the error page, retrieving the shared page handler | ||
| // instance, and working with it to add new data tables | ||
| $app['whoops.silex_info_handler'] = $app->protect(function () use ($app) { | ||
| try { | ||
| /** @var Request $request */ | ||
| $request = $app['request']; | ||
| } catch (RuntimeException $e) { | ||
| // This error occurred too early in the application's life | ||
| // and the request instance is not yet available. | ||
| return; | ||
| } | ||
|
|
||
| /** @var Handler $errorPageHandler */ | ||
| $errorPageHandler = $app["whoops.error_page_handler"]; | ||
|
|
||
| if ($errorPageHandler instanceof PrettyPageHandler) { | ||
| /** @var PrettyPageHandler $errorPageHandler */ | ||
|
|
||
| // General application info: | ||
| $errorPageHandler->addDataTable('Silex Application', array( | ||
| 'Charset' => $app['charset'], | ||
| 'Locale' => $app['locale'], | ||
| 'Route Class' => $app['route_class'], | ||
| 'Dispatcher Class' => $app['dispatcher_class'], | ||
| 'Application Class' => get_class($app), | ||
| )); | ||
|
|
||
| // Request info: | ||
| $errorPageHandler->addDataTable('Silex Application (Request)', array( | ||
| 'URI' => $request->getUri(), | ||
| 'Request URI' => $request->getRequestUri(), | ||
| 'Path Info' => $request->getPathInfo(), | ||
| 'Query String' => $request->getQueryString() ?: '<none>', | ||
| 'HTTP Method' => $request->getMethod(), | ||
| 'Script Name' => $request->getScriptName(), | ||
| 'Base Path' => $request->getBasePath(), | ||
| 'Base URL' => $request->getBaseUrl(), | ||
| 'Scheme' => $request->getScheme(), | ||
| 'Port' => $request->getPort(), | ||
| 'Host' => $request->getHost(), | ||
| )); | ||
| } | ||
| }); | ||
|
|
||
| $app['whoops'] = $app->share(function () use ($app) { | ||
| $run = new Run(); | ||
| $run->allowQuit(false); | ||
| $run->pushHandler($app['whoops.error_page_handler']); | ||
| $run->pushHandler($app['whoops.silex_info_handler']); | ||
| return $run; | ||
| }); | ||
|
|
||
| $app->error(function ($e) use ($app) { | ||
| $method = Run::EXCEPTION_HANDLER; | ||
|
|
||
| ob_start(); | ||
| $app['whoops']->$method($e); | ||
| $response = ob_get_clean(); | ||
| $code = $e instanceof HttpException ? $e->getStatusCode() : 500; | ||
|
|
||
| return new Response($response, $code); | ||
| }); | ||
|
|
||
| $app['whoops']->register(); | ||
| } | ||
|
|
||
| /** | ||
| * @see Silex\ServiceProviderInterface::boot | ||
| */ | ||
| public function boot(Application $app) | ||
| { | ||
| } | ||
| } |
| @@ -0,0 +1,86 @@ | ||
| Zepto(function($) { | ||
| prettyPrint(); | ||
|
|
||
| var $frameContainer = $('.frames-container'); | ||
| var $container = $('.details-container'); | ||
| var $activeLine = $frameContainer.find('.frame.active'); | ||
| var $activeFrame = $container.find('.frame-code.active'); | ||
| var $ajaxEditors = $('.editor-link[data-ajax]'); | ||
| var headerHeight = $('header').height(); | ||
|
|
||
| var highlightCurrentLine = function() { | ||
| // Highlight the active and neighboring lines for this frame: | ||
| var activeLineNumber = +($activeLine.find('.frame-line').text()); | ||
| var $lines = $activeFrame.find('.linenums li'); | ||
| var firstLine = +($lines.first().val()); | ||
|
|
||
| $($lines[activeLineNumber - firstLine - 1]).addClass('current'); | ||
| $($lines[activeLineNumber - firstLine]).addClass('current active'); | ||
| $($lines[activeLineNumber - firstLine + 1]).addClass('current'); | ||
| } | ||
|
|
||
| // Highlight the active for the first frame: | ||
| highlightCurrentLine(); | ||
|
|
||
| $frameContainer.on('click', '.frame', function() { | ||
| var $this = $(this); | ||
| var id = /frame\-line\-([\d]*)/.exec($this.attr('id'))[1]; | ||
| var $codeFrame = $('#frame-code-' + id); | ||
|
|
||
| if ($codeFrame) { | ||
| $activeLine.removeClass('active'); | ||
| $activeFrame.removeClass('active'); | ||
|
|
||
| $this.addClass('active'); | ||
| $codeFrame.addClass('active'); | ||
|
|
||
| $activeLine = $this; | ||
| $activeFrame = $codeFrame; | ||
|
|
||
| highlightCurrentLine(); | ||
|
|
||
| $container.scrollTop(headerHeight); | ||
| } | ||
| }); | ||
|
|
||
| if (typeof ZeroClipboard !== "undefined") { | ||
| ZeroClipboard.config({ | ||
| moviePath: '//ajax.cdnjs.com/ajax/libs/zeroclipboard/1.3.5/ZeroClipboard.swf', | ||
| }); | ||
|
|
||
| var clipEl = document.getElementById("copy-button"); | ||
| var clip = new ZeroClipboard( clipEl ); | ||
| var $clipEl = $(clipEl); | ||
|
|
||
| // show the button, when swf could be loaded successfully from CDN | ||
| clip.on("load", function() { | ||
| $clipEl.show(); | ||
| }); | ||
| } | ||
|
|
||
| $(document).on('keydown', function(e) { | ||
| if(e.ctrlKey) { | ||
| // CTRL+Arrow-UP/Arrow-Down support: | ||
| // 1) select the next/prev element | ||
| // 2) make sure the newly selected element is within the view-scope | ||
| // 3) focus the (right) container, so arrow-up/down (without ctrl) scroll the details | ||
| if (e.which === 38 /* arrow up */) { | ||
| $activeLine.prev('.frame').click(); | ||
| $activeLine[0].scrollIntoView(); | ||
| $container.focus(); | ||
| e.preventDefault(); | ||
| } else if (e.which === 40 /* arrow down */) { | ||
| $activeLine.next('.frame').click(); | ||
| $activeLine[0].scrollIntoView(); | ||
| $container.focus(); | ||
| e.preventDefault(); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| // Avoid to quit the page with some protocol (e.g. IntelliJ Platform REST API) | ||
| $ajaxEditors.on('click', function(e){ | ||
| e.preventDefault(); | ||
| $.get(this.href); | ||
| }); | ||
| }); |
| @@ -0,0 +1,40 @@ | ||
| <?php /* List data-table values, i.e: $_SERVER, $_GET, .... */ ?> | ||
| <div class="details"> | ||
| <div class="data-table-container" id="data-tables"> | ||
| <?php foreach ($tables as $label => $data): ?> | ||
| <div class="data-table" id="sg-<?php echo $tpl->escape($tpl->slug($label)) ?>"> | ||
| <?php if (!empty($data)): ?> | ||
| <label><?php echo $tpl->escape($label) ?></label> | ||
| <table class="data-table"> | ||
| <thead> | ||
| <tr> | ||
| <td class="data-table-k">Key</td> | ||
| <td class="data-table-v">Value</td> | ||
| </tr> | ||
| </thead> | ||
| <?php foreach ($data as $k => $value): ?> | ||
| <tr> | ||
| <td><?php echo $tpl->escape($k) ?></td> | ||
| <td><?php echo $tpl->escape(print_r($value, true)) ?></td> | ||
| </tr> | ||
| <?php endforeach ?> | ||
| </table> | ||
| <?php else: ?> | ||
| <label class="empty"><?php echo $tpl->escape($label) ?></label> | ||
| <span class="empty">empty</span> | ||
| <?php endif ?> | ||
| </div> | ||
| <?php endforeach ?> | ||
| </div> | ||
|
|
||
| <?php /* List registered handlers, in order of first to last registered */ ?> | ||
| <div class="data-table-container" id="handlers"> | ||
| <label>Registered Handlers</label> | ||
| <?php foreach ($handlers as $i => $handler): ?> | ||
| <div class="handler <?php echo ($handler === $handler) ? 'active' : ''?>"> | ||
| <?php echo $i ?>. <?php echo $tpl->escape(get_class($handler)) ?> | ||
| </div> | ||
| <?php endforeach ?> | ||
| </div> | ||
|
|
||
| </div> |
| @@ -0,0 +1,52 @@ | ||
| <?php /* Display a code block for all frames in the stack. | ||
| * @todo: This should PROBABLY be done on-demand, lest | ||
| * we get 200 frames to process. */ ?> | ||
| <div class="frame-code-container <?php echo (!$has_frames ? 'empty' : '') ?>"> | ||
| <?php foreach ($frames as $i => $frame): ?> | ||
| <?php $line = $frame->getLine(); ?> | ||
| <div class="frame-code <?php echo ($i == 0 ) ? 'active' : '' ?>" id="frame-code-<?php echo $i ?>"> | ||
| <div class="frame-file"> | ||
| <?php $filePath = $frame->getFile(); ?> | ||
| <?php if ($filePath && $editorHref = $handler->getEditorHref($filePath, (int) $line)): ?> | ||
| Open: | ||
| <a href="<?php echo $editorHref ?>" class="editor-link"<?php echo ($handler->getEditorAjax($filePath, (int) $line) ? ' data-ajax' : '') ?>> | ||
| <strong><?php echo $tpl->escape($filePath ?: '<#unknown>') ?></strong> | ||
| </a> | ||
| <?php else: ?> | ||
| <strong><?php echo $tpl->escape($filePath ?: '<#unknown>') ?></strong> | ||
| <?php endif ?> | ||
| </div> | ||
| <?php | ||
| // Do nothing if there's no line to work off | ||
| if ($line !== null): | ||
|
|
||
| // the $line is 1-indexed, we nab -1 where needed to account for this | ||
| $range = $frame->getFileLines($line - 8, 10); | ||
|
|
||
| // getFileLines can return null if there is no source code | ||
| if ($range): | ||
| $range = array_map(function ($line) { return empty($line) ? ' ' : $line;}, $range); | ||
| $start = key($range) + 1; | ||
| $code = join("\n", $range); | ||
| ?> | ||
| <pre class="code-block prettyprint linenums:<?php echo $start ?>"><?php echo $tpl->escape($code) ?></pre> | ||
| <?php endif ?> | ||
| <?php endif ?> | ||
|
|
||
| <?php | ||
| // Append comments for this frame | ||
| $comments = $frame->getComments(); | ||
| ?> | ||
| <div class="frame-comments <?php echo empty($comments) ? 'empty' : '' ?>"> | ||
| <?php foreach ($comments as $commentNo => $comment): ?> | ||
| <?php extract($comment) ?> | ||
| <div class="frame-comment" id="comment-<?php echo $i . '-' . $commentNo ?>"> | ||
| <span class="frame-comment-context"><?php echo $tpl->escape($context) ?></span> | ||
| <?php echo $tpl->escapeButPreserveUris($comment) ?> | ||
| </div> | ||
| <?php endforeach ?> | ||
| </div> | ||
|
|
||
| </div> | ||
| <?php endforeach ?> | ||
| </div> |
| @@ -0,0 +1,17 @@ | ||
| <?php /* List file names & line numbers for all stack frames; | ||
| clicking these links/buttons will display the code view | ||
| for that particular frame */ ?> | ||
| <?php foreach ($frames as $i => $frame): ?> | ||
| <div class="frame <?php echo ($i == 0 ? 'active' : '') ?>" id="frame-line-<?php echo $i ?>"> | ||
| <div class="frame-method-info"> | ||
| <span class="frame-index"><?php echo (count($frames) - $i - 1) ?>.</span> | ||
| <span class="frame-class"><?php echo $tpl->escape($frame->getClass() ?: '') ?></span> | ||
| <span class="frame-function"><?php echo $tpl->escape($frame->getFunction() ?: '') ?></span> | ||
| </div> | ||
|
|
||
| <span class="frame-file"> | ||
| <?php echo ($frame->getFile(true) ?: '<#unknown>') ?><!-- | ||
| --><span class="frame-line"><?php echo (int) $frame->getLine() ?></span> | ||
| </span> | ||
| </div> | ||
| <?php endforeach ?> |
| @@ -0,0 +1,34 @@ | ||
| <div class="exception"> | ||
| <h3 class="exc-title"> | ||
| <?php foreach ($name as $i => $nameSection): ?> | ||
| <?php if ($i == count($name) - 1): ?> | ||
| <span class="exc-title-primary"><?php echo $tpl->escape($nameSection) ?></span> | ||
| <?php else: ?> | ||
| <?php echo $tpl->escape($nameSection) . ' \\' ?> | ||
| <?php endif ?> | ||
| <?php endforeach ?> | ||
| <?php if ($code): ?> | ||
| <span title="Exception Code">(<?php echo $tpl->escape($code) ?>)</span> | ||
| <?php endif ?> | ||
| </h3> | ||
|
|
||
| <div class="help"> | ||
| <button title="show help">HELP</button> | ||
|
|
||
| <div id="help-overlay"> | ||
| <div id="help-framestack">Callstack information; navigate with mouse or keyboard using <kbd>Ctrl+↑</kbd> or <kbd>Ctrl+↓</kbd></div> | ||
| <div id="help-clipboard">Copy-to-clipboard button</div> | ||
| <div id="help-exc-message">Exception message and its type</div> | ||
| <div id="help-code">Code snippet where the error was thrown</div> | ||
| <div id="help-request">Server state information</div> | ||
| <div id="help-appinfo">Application provided context information</div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <button id="copy-button" class="clipboard" data-clipboard-target="plain-exception" title="copy exception into clipboard"></button> | ||
| <span id="plain-exception"><?php echo $tpl->escape($plain_exception) ?></span> | ||
|
|
||
| <p class="exc-message"> | ||
| <?php echo $tpl->escape($message) ?> | ||
| </p> | ||
| </div> |
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
| /** | ||
| * Layout template file for Whoops's pretty error output. | ||
| */ | ||
| ?> | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <title><?php echo $tpl->escape($page_title) ?></title> | ||
|
|
||
| <style><?php echo $stylesheet ?></style> | ||
| </head> | ||
| <body> | ||
|
|
||
| <div class="Whoops container"> | ||
|
|
||
| <div class="stack-container"> | ||
| <div class="frames-container cf <?php echo (!$has_frames ? 'empty' : '') ?>"> | ||
| <?php $tpl->render($frame_list) ?> | ||
| </div> | ||
| <div class="details-container cf"> | ||
| <header> | ||
| <?php $tpl->render($header) ?> | ||
| </header> | ||
| <?php $tpl->render($frame_code) ?> | ||
| <?php $tpl->render($env_details) ?> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <script src="//cdnjs.cloudflare.com/ajax/libs/zeroclipboard/1.3.5/ZeroClipboard.min.js"></script> | ||
| <script src="//cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.js"></script> | ||
| <script><?php echo $zepto ?></script> | ||
| <script><?php echo $javascript ?></script> | ||
| </body> | ||
| </html> |