Large diffs are not rendered by default.

@@ -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;
}
}

Large diffs are not rendered by default.

@@ -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.

Large diffs are not rendered by default.

@@ -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);
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -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());
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -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.

[![Build Status](https://travis-ci.org/doctrine/inflector.svg?branch=master)](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"
}
}
}

Large diffs are not rendered by default.

@@ -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, "&hellip;", $file);
$file = str_replace("/", "/&shy;", $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;
}
}

Large diffs are not rendered by default.

@@ -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('&', '&amp;', 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)
{
}
}

Large diffs are not rendered by default.

@@ -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);
});
});

Large diffs are not rendered by default.

@@ -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+&uparrow;</kbd> or <kbd>Ctrl+&downarrow;</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>