Skip to content

Commit

Permalink
Merge branch 'master' into 4.next
Browse files Browse the repository at this point in the history
  • Loading branch information
markstory committed Aug 23, 2020
2 parents 9cdfe12 + 9915398 commit 201ea18
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ script:
after_success:
- |
if [[ $TRAVIS_PHP_VERSION == '7.4' ]]; then
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.1.0/php-coveralls.phar
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.2.0/php-coveralls.phar
chmod +x php-coveralls.phar
./php-coveralls.phar
fi
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"@phpstan",
"@psalm"
],
"stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:0.12.36 psalm/phar:~3.13.1 && mv composer.backup composer.json"
"stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:0.12.38 psalm/phar:~3.13.1 && mv composer.backup composer.json"
},
"config": {
"sort-packages": true,
Expand Down
2 changes: 2 additions & 0 deletions src/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
* @property \Cake\Controller\Component\FormProtectionComponent $FormProtection
* @property \Cake\Controller\Component\PaginatorComponent $Paginator
* @property \Cake\Controller\Component\RequestHandlerComponent $RequestHandler
* @property \Cake\Controller\Component\SecurityComponent $Security
* @property \Cake\Controller\Component\AuthComponent $Auth
* @link https://book.cakephp.org/4/en/controllers.html
*/
class Controller implements EventListenerInterface, EventDispatcherInterface
Expand Down
35 changes: 23 additions & 12 deletions src/Error/Debug/HtmlFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ protected function dumpHeader(): string
*/
public function dump(NodeInterface $node): string
{
$html = $this->export($node);
$html = $this->export($node, 0);
$head = '';
if (!static::$outputHeader) {
static::$outputHeader = true;
Expand All @@ -119,9 +119,10 @@ public function dump(NodeInterface $node): string
* Convert a tree of NodeInterface objects into HTML
*
* @param \Cake\Error\Debug\NodeInterface $var The node tree to dump.
* @param int $indent The current indentation level.
* @return string
*/
protected function export(NodeInterface $var): string
protected function export(NodeInterface $var, int $indent): string
{
if ($var instanceof ScalarNode) {
switch ($var->getType()) {
Expand All @@ -140,10 +141,10 @@ protected function export(NodeInterface $var): string
}
}
if ($var instanceof ArrayNode) {
return $this->exportArray($var);
return $this->exportArray($var, $indent + 1);
}
if ($var instanceof ClassNode || $var instanceof ReferenceNode) {
return $this->exportObject($var);
return $this->exportObject($var, $indent + 1);
}
if ($var instanceof SpecialNode) {
return $this->style('special', (string)$var->getValue());
Expand All @@ -155,25 +156,29 @@ protected function export(NodeInterface $var): string
* Export an array type object
*
* @param \Cake\Error\Debug\ArrayNode $var The array to export.
* @param int $indent The current indentation level.
* @return string Exported array.
*/
protected function exportArray(ArrayNode $var): string
protected function exportArray(ArrayNode $var, int $indent): string
{
$open = '<span class="cake-dbg-array">' .
$this->style('punct', '[') .
'<samp class="cake-dbg-array-items">';
$vars = [];
$break = "\n" . str_repeat(' ', $indent);
$endBreak = "\n" . str_repeat(' ', $indent - 1);

$arrow = $this->style('punct', ' => ');
foreach ($var->getChildren() as $item) {
$val = $item->getValue();
$vars[] = '<span class="cake-dbg-array-item">' .
$this->export($item->getKey()) . $arrow . $this->export($val) .
$vars[] = $break . '<span class="cake-dbg-array-item">' .
$this->export($item->getKey(), $indent) . $arrow . $this->export($val, $indent) .
$this->style('punct', ',') .
'</span>';
}

$close = '</samp>' .
$endBreak .
$this->style('punct', ']') .
'</span>';

Expand All @@ -184,16 +189,19 @@ protected function exportArray(ArrayNode $var): string
* Handles object to string conversion.
*
* @param \Cake\Error\Debug\ClassNode|\Cake\Error\Debug\ReferenceNode $var Object to convert.
* @param int $indent The current indentation level.
* @return string
* @see \Cake\Error\Debugger::exportVar()
*/
protected function exportObject($var): string
protected function exportObject($var, int $indent): string
{
$objectId = "cake-db-object-{$this->id}-{$var->getId()}";
$out = sprintf(
'<span class="cake-dbg-object" id="%s">',
$objectId
);
$break = "\n" . str_repeat(' ', $indent);
$endBreak = "\n" . str_repeat(' ', $indent - 1);

if ($var instanceof ReferenceNode) {
$link = sprintf(
Expand Down Expand Up @@ -224,23 +232,26 @@ protected function exportObject($var): string
$visibility = $property->getVisibility();
$name = $property->getName();
if ($visibility && $visibility !== 'public') {
$props[] = '<span class="cake-dbg-prop">' .
$props[] = $break .
'<span class="cake-dbg-prop">' .
$this->style('visibility', $visibility) .
' ' .
$this->style('property', $name) .
$arrow .
$this->export($property->getValue()) .
$this->export($property->getValue(), $indent) .
'</span>';
} else {
$props[] = '<span class="cake-dbg-prop">' .
$props[] = $break .
'<span class="cake-dbg-prop">' .
$this->style('property', $name) .
$arrow .
$this->export($property->getValue()) .
$this->export($property->getValue(), $indent) .
'</span>';
}
}

$end = '</samp>' .
$endBreak .
$this->style('punct', '}') .
'</span>';

Expand Down
63 changes: 52 additions & 11 deletions src/Error/Debug/dumpHeader.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.cake-debug {
--color-bg: #ECECE9;
--color-highlight-bg: #fcf8e3;
--color-control-bg: hsla(0, 0%, 50%, 0.2);

--color-orange: #c44f24;
--color-green: #0b6125;
Expand All @@ -21,6 +22,7 @@
line-height: 16px;
font-size: 14px;
margin-bottom: 10px;
position: relative;
}
.cake-debug:last-child {
margin-bottom: 0;
Expand Down Expand Up @@ -58,21 +60,33 @@
[data-hidden=true] {
display: none;
}
.cake-dbg-collapse:before {
content: "\25b8";

.cake-dbg-collapse {
display: inline-block;
width: 10px;
height: 10px;
font-size: 13px;
line-height: 10px;
background: hsla(0, 0%, 50%, 0.2);
padding: 4px 2px 4px 6px;
width: 14px;
height: 14px;
vertical-align: middle;
border-radius: 3px;
color: var(--color-blue);

background: var(--color-control-bg);
/* Image is an rawurlencoded SVG */
background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2212%22%20width%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Cpolygon%20points%3D%223%2C1%203%2C11%208%2C6%22 style%3D%22fill%3A%234070a0%3B%22%2F%3E%3C%2Fsvg%3E");
background-position: 2px 1px;
background-repeat: no-repeat;
}
.cake-dbg-collapse[data-open=true]:before {
content: "\25be";
padding: 4px 3px 4px 5px;
.cake-dbg-collapse[data-open=true] {
transform: rotate(90deg);
}
/* Copy button */
.cake-dbg-copy {
position: absolute;
top: 0px;
right: 0px;
padding: 6px;
background: var(--color-control-bg);
color: var(--color-blue);
border-radius: 0 0 0 3px;
}

/* Textual elements */
Expand Down Expand Up @@ -116,6 +130,7 @@
createCollapsibles(doc.querySelectorAll('.cake-dbg-object-props'));
attachRefEvents(doc.querySelectorAll('.cake-dbg'));
openBlocks(doc.querySelectorAll('.cake-debug[data-open-all="true"]'));
attachCopyButton(doc.querySelectorAll('.cake-dbg'));
}
// Add a name on window so DebugKit can add controls to dump blocks
win.__cakeDebugBlockInit = initialize;
Expand Down Expand Up @@ -211,6 +226,32 @@
}
}

function attachCopyButton(nodes) {
nodes.forEach(function (container) {
var copy = doc.createElement('a');
copy.classList.add('cake-dbg-copy');
copy.setAttribute('href', '#');
copy.setAttribute('title', 'Copy contents of debug output');
copy.appendChild(doc.createTextNode('Copy'));

// Add copy behavior
copy.addEventListener('click', function (event) {
event.preventDefault();
event.stopPropagation();
var lineNo = '';
if (container.parentNode && container.parentNode.classList.contains('cake-debug')) {
var line = container.parentNode.querySelector('span');
lineNo = line.textContent + "\n";
}

// Chop off last 4 to exclude copy button text.
navigator.clipboard.writeText(lineNo + container.textContent.substring(0, container.textContent.length - 4));
});

container.appendChild(copy);
});
}

doc.addEventListener('DOMContentLoaded', initialize);
}(window, document))
</script>
44 changes: 36 additions & 8 deletions src/Routing/Route/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,13 @@ class Route
* - `_host` - Define the host name pattern if you want this route to only match
* specific host names. You can use `.*` and to create wildcard subdomains/hosts
* e.g. `*.example.com` matches all subdomains on `example.com`.
* - `_method` - Defines the HTTP method(s) the route applies to. It can be
* a string or array of valid HTTP method name.
*
* @param string $template Template string with parameter placeholders
* @param array $defaults Defaults for the route.
* @param array $options Array of additional options for the Route
* @throws \InvalidArgumentException When `$options['_method']` are not in `VALID_METHODS` list.
*/
public function __construct(string $template, array $defaults = [], array $options = [])
{
Expand All @@ -131,6 +134,10 @@ public function __construct(string $template, array $defaults = [], array $optio
$this->setExtensions((array)$this->options['_ext']);
$this->setMiddleware((array)$this->options['_middleware']);
unset($this->options['_middleware']);

if (isset($this->defaults['_method'])) {
$this->defaults['_method'] = $this->normalizeAndValidateMethods($this->defaults['_method']);
}
}

/**
Expand Down Expand Up @@ -164,20 +171,36 @@ public function getExtensions(): array
*
* @param string[] $methods The HTTP methods to accept.
* @return $this
* @throws \InvalidArgumentException
* @throws \InvalidArgumentException When methods are not in `VALID_METHODS` list.
*/
public function setMethods(array $methods)
{
$methods = array_map('strtoupper', $methods);
$diff = array_diff($methods, static::VALID_METHODS);
$this->defaults['_method'] = $this->normalizeAndValidateMethods($methods);

return $this;
}

/**
* Normalize method names to upper case and validate that they are valid HTTP methods.
*
* @param string|string[] $methods Methods.
* @return string|string[]
* @throws \InvalidArgumentException When methods are not in `VALID_METHODS` list.
*/
protected function normalizeAndValidateMethods($methods)
{
$methods = is_array($methods)
? array_map('strtoupper', $methods)
: strtoupper($methods);

$diff = array_diff((array)$methods, static::VALID_METHODS);
if ($diff !== []) {
throw new InvalidArgumentException(
sprintf('Invalid HTTP method received. %s is invalid.', implode(', ', $diff))
sprintf('Invalid HTTP method received. `%s` is invalid.', implode(', ', $diff))
);
}
$this->defaults['_method'] = $methods;

return $this;
return $methods;
}

/**
Expand Down Expand Up @@ -417,9 +440,13 @@ public function parseRequest(ServerRequestInterface $request): ?array
* @param string $url The URL to attempt to parse.
* @param string $method The HTTP method of the request being parsed.
* @return array|null An array of request parameters, or null on failure.
* @throws \InvalidArgumentException When method is not an empty string or in `VALID_METHODS` list.
*/
public function parse(string $url, string $method): ?array
{
if ($method !== '') {
$method = $this->normalizeAndValidateMethods($method);
}
$compiledRoute = $this->compile();
[$url, $ext] = $this->_parseExtension($url);

Expand Down Expand Up @@ -740,9 +767,10 @@ protected function _matchMethod(array $url): bool
if (empty($url['_method'])) {
$url['_method'] = 'GET';
}
$methods = array_map('strtoupper', (array)$url['_method']);
$defaults = (array)$this->defaults['_method'];
$methods = (array)$this->normalizeAndValidateMethods($url['_method']);
foreach ($methods as $value) {
if (in_array($value, (array)$this->defaults['_method'], true)) {
if (in_array($value, $defaults, true)) {
return true;
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/Routing/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ class Router
* parameters to the route collection.
*
* @var callable[]
* @psalm-var array<int, (\Closure|callable-string)>
*/
protected static $_urlFilters = [];

Expand Down Expand Up @@ -332,7 +331,6 @@ public static function resetRoutes(): void
*
* @param callable $function The function to add
* @return void
* @psalm-param \Closure|callable-string $function
*/
public static function addUrlFilter(callable $function): void
{
Expand All @@ -357,6 +355,7 @@ protected static function _applyUrlFilters(array $url): array
if (is_array($filter)) {
$ref = new ReflectionMethod($filter[0], $filter[1]);
} else {
/** @psalm-var \Closure|callable-string $filter */
$ref = new ReflectionFunction($filter);
}
$message = sprintf(
Expand Down
19 changes: 12 additions & 7 deletions tests/TestCase/Error/Debug/HtmlFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,18 @@ public function testDump()
$this->assertGreaterThan(0, count($dom->childNodes));

$expected = <<<TEXT
object(MyObject) id:1 {stringProp =&gt; &#039;value&#039;
protected intProp =&gt; (int) 1
protected floatProp =&gt; (float) 1.1
protected boolProp =&gt; true
private nullProp =&gt; null
arrayProp =&gt; [&#039;&#039; =&gt; too much,(int) 1 =&gt; object(MyObject) id: 1 {},]}
object(MyObject) id:1 {
stringProp =&gt; &#039;value&#039;
protected intProp =&gt; (int) 1
protected floatProp =&gt; (float) 1.1
protected boolProp =&gt; true
private nullProp =&gt; null
arrayProp =&gt; [
&#039;&#039; =&gt; too much,
(int) 1 =&gt; object(MyObject) id: 1 {},
]
}
TEXT;
$this->assertStringContainsString(str_replace("\n", '', $expected), strip_tags($result));
$this->assertStringContainsString($expected, strip_tags($result));
}
}
Loading

0 comments on commit 201ea18

Please sign in to comment.