diff --git a/README.md b/README.md index 3bea72d..a66ae4b 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ class DataModelHelper - [mapOf](#mapof): Create a map of any type by using - [pregReplace](#pregreplace): Perform a regular expression search and replace. +- [pregMatch](#pregmatch): Perform a regular expression match. - [isUrl](#isurl): Validates a url. ### `mapOf` @@ -444,6 +445,33 @@ $User = User::from([ echo $User->name; // Outputs: 'Trophy!' ``` +### `pregMatch` + +Use `pregMatch` to perform a regular expression match. + +```php +class User +{ + use \Zerotoprod\DataModel\DataModel; + use \Zerotoprod\DataModelHelper\DataModelHelper; + + #[Describe([ + 'cast' => [self::class, 'pregMatch'], + 'pattern' => '/s/', // Required + 'match_on' => 0 // Index of the $matches to return + 'flags' => PREG_UNMATCHED_AS_NULL + 'offset' => 0 + ])] + public string $name; +} + +$User = User::from([ + 'name' => 'sarah', +]); + +echo $User->name; // Outputs: 's' +``` + ### `isUrl` Use `isUrl` to perform validate a url. diff --git a/src/DataModelHelper.php b/src/DataModelHelper.php index 13d5b56..d5658b3 100644 --- a/src/DataModelHelper.php +++ b/src/DataModelHelper.php @@ -110,11 +110,48 @@ public static function pregReplace(mixed $value, array $context, ?ReflectionAttr ? null : ''; } + $args = $Attribute?->getArguments()[0]; return preg_replace($args['pattern'], $args['replacement'] ?? '', $value); } + /** + * Perform a regular expression match. + * + * NOTE: If property allows null, null will be returned. + * + * ``` + * #[Describe([ + * 'cast' => [self::class, 'pregMatch'], + * 'pattern' => '/s/', // Required + * 'match_on' => 0 // Index of the $matches to return + * 'flags' => PREG_UNMATCHED_AS_NULL + * 'offset' => 0 + * ])] + * ``` + */ + public static function pregMatch(mixed $value, array $context, ?ReflectionAttribute $Attribute, ReflectionProperty $Property) + { + if (!$value && $Property->getType()?->allowsNull()) { + return null; + } + + if (!is_string($value)) { + return $value; + } + + $args = $Attribute?->getArguments()[0]; + preg_match($args['pattern'], $value, $matches, $args['flags'] ?? 0, $args['offset'] ?? 0); + + if(isset($args['match_on']) && !isset($matches[$args['match_on']])) { + return; + } + return isset($args['match_on']) + ? $matches[$args['match_on']] + : $matches; + } + /** * Determine if a given value is a valid URL. * ``` diff --git a/tests/Unit/PregMatch/PregMatchTest.php b/tests/Unit/PregMatch/PregMatchTest.php new file mode 100644 index 0000000..cd94260 --- /dev/null +++ b/tests/Unit/PregMatch/PregMatchTest.php @@ -0,0 +1,31 @@ + 'stop', + User::s => 'stop', + User::as_null => 'top', + User::offset => 'stop', + ]); + + self::assertEquals(['s'], $User->name); + self::assertEquals('s', $User->s); + self::assertNull($User->as_null); + self::assertEquals([], $User->offset); + } + + #[Test] public function allowsNull(): void + { + $User = User::from(); + + self::assertNull($User->name); + } +} \ No newline at end of file diff --git a/tests/Unit/PregMatch/User.php b/tests/Unit/PregMatch/User.php new file mode 100644 index 0000000..56247c5 --- /dev/null +++ b/tests/Unit/PregMatch/User.php @@ -0,0 +1,46 @@ + [self::class, 'pregMatch'], + 'pattern' => '/s/', + ])] + public ?array $name; + + #[Describe([ + 'cast' => [self::class, 'pregMatch'], + 'pattern' => '/s/', + 'match_on' => 0 + ])] + public ?string $s; + + #[Describe([ + 'cast' => [self::class, 'pregMatch'], + 'pattern' => '/s/', + 'match_on' => 0, + 'flags' => PREG_UNMATCHED_AS_NULL + ])] + public ?string $as_null; + + #[Describe([ + 'cast' => [self::class, 'pregMatch'], + 'pattern' => '/s/', + 'offset' => 1 + ])] + public ?array $offset; +} \ No newline at end of file