Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/Deserializer/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ function keyValue(Reader $reader, ?string $namespace = null): array
$values[$reader->localName] = $reader->parseCurrentElement()['value'];
} else {
$clark = $reader->getClark();
$values[$clark] = $reader->parseCurrentElement()['value'];
if (null !== $clark) {
$values[$clark] = $reader->parseCurrentElement()['value'];
}
}
} else {
if (!$reader->read()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/LibXMLException.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function __construct(/**
*/
protected array $errors, int $code = 0, ?\Throwable $previousException = null)
{
parent::__construct($this->errors[0]->message.' on line '.$this->errors[0]->line.', column '.$this->errors[0]->column, $code, $previousException);
parent::__construct(trim($this->errors[0]->message).' on line '.$this->errors[0]->line.', column '.$this->errors[0]->column, $code, $previousException);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion lib/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,9 @@ public function parseAttributes(): array
}

$name = $this->getClark();
$attributes[$name] = $this->value;
if (null !== $name) {
$attributes[$name] = $this->value;
}
} else {
$attributes[$this->localName] = $this->value;
}
Expand Down
48 changes: 32 additions & 16 deletions lib/Service.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,29 +109,45 @@ public function getWriter(): Writer
*/
public function parse($input, ?string $contextUri = null, ?string &$rootElementName = null)
{
if (!is_string($input)) {
// Unfortunately the XMLReader doesn't support streams. When it
// does, we can optimize this.
if (PHP_VERSION_ID >= 80400) {
if (is_resource($input)) {
$input = (string) stream_get_contents($input);
$r = Reader::fromStream($input, null, $this->options);
} elseif (is_string($input)) {
if ('' === $input) {
throw new ParseException('The input element to parse is empty. Do not attempt to parse');
}
$r = Reader::fromString($input, null, $this->options);
} else {
// Input is not a string and not a resource.
// Therefore, it has to be a closed resource.
// Effectively empty input has been passed in.
$input = '';
throw new ParseException('The input element to parse is empty. Do not attempt to parse');
}
}

// If input is empty, then it's safe to throw an exception
if ('' === $input) {
throw new ParseException('The input element to parse is empty. Do not attempt to parse');
}
$r->elementMap = $this->elementMap;
$r->contextUri = $contextUri;
} else {
if (!is_string($input)) {
// Unfortunately the XMLReader doesn't support streams. When it
// does, we can optimize this.
if (is_resource($input)) {
$input = (string) stream_get_contents($input);
} else {
// Input is not a string and not a resource.
// Therefore, it has to be a closed resource.
// Effectively empty input has been passed in.
$input = '';
}
}
// If input is empty, then it's safe to throw an exception
if ('' === $input) {
throw new ParseException('The input element to parse is empty. Do not attempt to parse');
}

$r = $this->getReader();
$r->contextUri = $contextUri;
$r->XML($input, null, $this->options);
$r = $this->getReader();
$r->contextUri = $contextUri;
$r->XML($input, null, $this->options);
}

$result = $r->parse();

$rootElementName = $result['name'];

return $result['value'];
Expand Down
2 changes: 2 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ parameters:
message: "#^Call to function is_null\\(\\) with null will always evaluate to true\\.$#"
path: lib/Serializer/functions.php
count: 1
- message: '#Dynamic call to static method PHPUnit\\Framework\\Assert::.*\(\).#'
path: tests/Sabre/Xml/ServiceTest.php
10 changes: 10 additions & 0 deletions phpstan-rules-lt-8.4.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
parameters:
ignoreErrors:
-
rawMessage: 'Call to an undefined static method Sabre\Xml\Reader::fromStream().'
path: lib/Service.php
count: 1
-
rawMessage: 'Call to an undefined static method Sabre\Xml\Reader::fromString().'
path: lib/Service.php
count: 1
20 changes: 20 additions & 0 deletions phpstan.conditional.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

/**
* Add conditionally extra phpstan rules
* Typically used for specific PHP versions
* Must be called in main PHPStan neon config file.
*/
$includes = [];

if (PHP_VERSION_ID < 80400) {
$includes[] = __DIR__.'/phpstan-rules-lt-8.4.neon';
}

$config = [];
$config['includes'] = $includes;
$config['parameters']['phpVersion'] = PHP_VERSION_ID;

return $config;
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
includes:
- phpstan-baseline.neon
- phpstan.conditional.php

parameters:
level: 8
Expand Down
21 changes: 19 additions & 2 deletions tests/Sabre/Xml/ServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ public function testEmptyInputParse($input): void
$util->parse($input, '/sabre.io/ns');
}

public function testEmptyStream(): void
{
if (PHP_VERSION_ID < 80400) {
$this->markTestSkipped('This test requires PHP 8.4.');
}

$this->expectException(LibXMLException::class);
$this->expectExceptionMessage('Document is empty on line 1, column 1');

$emptyResource = fopen('php://input', 'r');
$this->assertNotFalse($emptyResource);
$util = new Service();
$util->parse($emptyResource, '/sabre.io/ns');
}

#[Depends('testGetReader')]
public function testParse(): void
{
Expand Down Expand Up @@ -364,9 +379,11 @@ public static function provideParseClarkNotationInput(): iterable
*/
public static function providesEmptyInput(): array
{
$emptyResource = fopen('php://input', 'r');
$data = [];
$data[] = [$emptyResource];
if (PHP_VERSION_ID < 80400) {
Comment thread
CarlSchwan marked this conversation as resolved.
$emptyResource = fopen('php://input', 'r');
$data[] = [$emptyResource];
}
$data[] = [''];

// Also test trying to parse a resource stream that has already been closed.
Expand Down
Loading