From acf0abc6fd281abaa29894a97610ef0a2d3ef30f Mon Sep 17 00:00:00 2001 From: Pavel Kacha Date: Mon, 16 Oct 2017 21:42:01 +0200 Subject: [PATCH 1/3] Add available flag FORCE_OBJECT for Json::encode() --- src/Utils/Json.php | 5 ++++- tests/Utils/Json.encode().phpt | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Utils/Json.php b/src/Utils/Json.php index 2b4f12570..c06e1f230 100644 --- a/src/Utils/Json.php +++ b/src/Utils/Json.php @@ -23,14 +23,17 @@ final class Json const PRETTY = 0b0010; + const FORCE_OBJECT = 0b10000; + /** - * Returns the JSON representation of a value. Accepts flag Json::PRETTY. + * Returns the JSON representation of a value. Accepts flag Json::PRETTY and JSON::FORCE_OBJECT. */ public static function encode($value, int $flags = 0): string { $flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | ($flags & self::PRETTY ? JSON_PRETTY_PRINT : 0) + | ($flags & self::FORCE_OBJECT ? JSON_FORCE_OBJECT : 0) | (defined('JSON_PRESERVE_ZERO_FRACTION') ? JSON_PRESERVE_ZERO_FRACTION : 0); // since PHP 5.6.6 & PECL JSON-C 1.3.7 $json = json_encode($value, $flags); diff --git a/tests/Utils/Json.encode().phpt b/tests/Utils/Json.encode().phpt index ba84bbfd1..9888baf1b 100644 --- a/tests/Utils/Json.encode().phpt +++ b/tests/Utils/Json.encode().phpt @@ -35,6 +35,8 @@ Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}")); // JSON_PRETTY_PRINT Assert::same("[\n 1,\n 2,\n 3\n]", Json::encode([1, 2, 3], Json::PRETTY)); +Assert::same('[]', JSON::encode([])); +Assert::same('{}', JSON::encode([], Json::FORCE_OBJECT)); Assert::exception(function () { Json::encode(NAN); From 7cd57f04ac92b39b6cef3abb972cb8b92a5a4bc2 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 26 May 2020 14:42:57 +0200 Subject: [PATCH 2/3] added Reflection::getMethodDeclaringMethod() --- src/Utils/Reflection.php | 35 ++++++ .../Reflection.getDeclaringMethod.alias.phpt | 112 ++++++++++++++++++ ...flection.getDeclaringMethod.insteadof.phpt | 67 +++++++++++ ...flection.getDeclaringMethod.overwrite.phpt | 66 +++++++++++ .../Utils/Reflection.getDeclaringMethod.phpt | 71 +++++++++++ 5 files changed, 351 insertions(+) create mode 100644 tests/Utils/Reflection.getDeclaringMethod.alias.phpt create mode 100644 tests/Utils/Reflection.getDeclaringMethod.insteadof.phpt create mode 100644 tests/Utils/Reflection.getDeclaringMethod.overwrite.phpt create mode 100644 tests/Utils/Reflection.getDeclaringMethod.phpt diff --git a/src/Utils/Reflection.php b/src/Utils/Reflection.php index 792197d21..fe7750137 100644 --- a/src/Utils/Reflection.php +++ b/src/Utils/Reflection.php @@ -113,6 +113,7 @@ public static function getPropertyDeclaringClass(\ReflectionProperty $prop): \Re { foreach ($prop->getDeclaringClass()->getTraits() as $trait) { if ($trait->hasProperty($prop->name) + // doc-comment guessing as workaround for insufficient PHP reflection && $trait->getProperty($prop->name)->getDocComment() === $prop->getDocComment() ) { return self::getPropertyDeclaringClass($trait->getProperty($prop->name)); @@ -122,6 +123,40 @@ public static function getPropertyDeclaringClass(\ReflectionProperty $prop): \Re } + /** + * Returns declaring class or trait. + */ + public static function getMethodDeclaringMethod(\ReflectionMethod $method): \ReflectionMethod + { + // file & line guessing as workaround for insufficient PHP reflection + $decl = $method->getDeclaringClass(); + if ($decl->getFileName() === $method->getFileName() + && $decl->getStartLine() <= $method->getStartLine() + && $decl->getEndLine() >= $method->getEndLine() + ) { + return $method; + } + + $hash = [$method->getFileName(), $method->getStartLine(), $method->getEndLine()]; + if (($alias = $decl->getTraitAliases()[$method->name] ?? null) + && ($m = new \ReflectionMethod($alias)) + && [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] === $hash + ) { + return self::getMethodDeclaringMethod($m); + } + + foreach ($decl->getTraits() as $trait) { + if ($trait->hasMethod($method->name) + && ($m = $trait->getMethod($method->name)) + && [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] === $hash + ) { + return self::getMethodDeclaringMethod($m); + } + } + return $method; + } + + /** * Are documentation comments available? */ diff --git a/tests/Utils/Reflection.getDeclaringMethod.alias.phpt b/tests/Utils/Reflection.getDeclaringMethod.alias.phpt new file mode 100644 index 000000000..a9b051658 --- /dev/null +++ b/tests/Utils/Reflection.getDeclaringMethod.alias.phpt @@ -0,0 +1,112 @@ +getDeclaringClass()->name . '::' . $res->name; +} + + +// new ReflectionMethod and getMethod returns different method names, PHP #79636 + +// Method in trait +Assert::same('A::foo', get((new ReflectionClass('E3'))->getMethod('foo3'))); +Assert::same('A::foo', get(new ReflectionMethod('E3', 'foo3'))); + +Assert::same('B2::foo2', get((new ReflectionClass('E3'))->getMethod('foo2'))); +Assert::same('B2::foo2', get(new ReflectionMethod('E3', 'foo2'))); + +Assert::same('A::foo', get((new ReflectionClass('E3'))->getMethod('foo'))); +Assert::same('A::foo', get(new ReflectionMethod('E3', 'foo'))); + +// Method in class +Assert::same('E2::alias', get((new ReflectionClass('E2'))->getMethod('alias'))); +Assert::same('E2::alias', get(new ReflectionMethod('E2', 'alias'))); + +Assert::same('E2::foo2', get((new ReflectionClass('E2'))->getMethod('foo2'))); +Assert::same('E2::foo2', get(new ReflectionMethod('E2', 'foo2'))); + +// Method in trait +Assert::same('A::foo', get((new ReflectionClass('E1'))->getMethod('alias'))); +Assert::same('A::foo', get(new ReflectionMethod('E1', 'alias'))); + +// Method in trait +Assert::same('B2::foo2', get((new ReflectionClass('B2'))->getMethod('foo2'))); +Assert::same('B2::foo2', get(new ReflectionMethod('B2', 'foo2'))); + +Assert::same('A::foo', get((new ReflectionClass('B'))->getMethod('foo2'))); +Assert::same('A::foo', get(new ReflectionMethod('B', 'foo2'))); + +Assert::same('A::foo', get((new ReflectionClass('A'))->getMethod('foo'))); +Assert::same('A::foo', get(new ReflectionMethod('A', 'foo'))); diff --git a/tests/Utils/Reflection.getDeclaringMethod.insteadof.phpt b/tests/Utils/Reflection.getDeclaringMethod.insteadof.phpt new file mode 100644 index 000000000..a1f541d03 --- /dev/null +++ b/tests/Utils/Reflection.getDeclaringMethod.insteadof.phpt @@ -0,0 +1,67 @@ +getDeclaringClass()->name . '::' . $res->name; +} + + +// Method in class +Assert::same('D::foo', get(new ReflectionMethod('D', 'foo'))); + +// Method in trait - uses doccomment & file-line workaround +Assert::same('B::foo', get(new ReflectionMethod('C', 'foo'))); + +// Method in trait +Assert::same('B::foo', get(new ReflectionMethod('B', 'foo'))); + +// Method in trait +Assert::same('A::foo', get(new ReflectionMethod('A', 'foo'))); diff --git a/tests/Utils/Reflection.getDeclaringMethod.overwrite.phpt b/tests/Utils/Reflection.getDeclaringMethod.overwrite.phpt new file mode 100644 index 000000000..cd46fa533 --- /dev/null +++ b/tests/Utils/Reflection.getDeclaringMethod.overwrite.phpt @@ -0,0 +1,66 @@ +getDeclaringClass()->name . '::' . $res->name; +} + + +// Method in class +Assert::same('D::foo', get(new ReflectionMethod('D', 'foo'))); + +// Method in class - uses doccomment & file-line workaround +Assert::same('C::foo', get(new ReflectionMethod('C', 'foo'))); + +// Method in trait - uses doccomment & file-line workaround +Assert::same('B::foo', get(new ReflectionMethod('B', 'foo'))); + +// Method in trait +Assert::same('A::foo', get(new ReflectionMethod('A', 'foo'))); diff --git a/tests/Utils/Reflection.getDeclaringMethod.phpt b/tests/Utils/Reflection.getDeclaringMethod.phpt new file mode 100644 index 000000000..274e86d50 --- /dev/null +++ b/tests/Utils/Reflection.getDeclaringMethod.phpt @@ -0,0 +1,71 @@ +getDeclaringClass()->name . '::' . $res->name; +} + + +// Method in trait +Assert::same('B::foo', get(new ReflectionMethod('D', 'foo'))); + +// Method in parent trait +Assert::same('A::bar', get(new ReflectionMethod('D', 'bar'))); + +// Method in class itself +Assert::same('C::own', get(new ReflectionMethod('D', 'own'))); + +// Method in second trait +Assert::same('E::baz', get(new ReflectionMethod('D', 'baz'))); From 636ab009f936814d20c9ada2853f2b7dc0aab8f6 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 20 May 2020 19:00:51 +0200 Subject: [PATCH 3/3] cs --- src/Utils/Validators.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Utils/Validators.php b/src/Utils/Validators.php index 664c8eaa2..3cf23f14a 100644 --- a/src/Utils/Validators.php +++ b/src/Utils/Validators.php @@ -301,12 +301,15 @@ public static function isEmail(string $value): bool { $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part $alpha = "a-z\x80-\xFF"; // superset of IDN - return (bool) preg_match("(^ - (\"([ !#-[\\]-~]*|\\\\[ -~])+\"|$atom+(\\.$atom+)*) # quoted or unquoted + return (bool) preg_match(<<