PHP code example of phphd / exceptional-validation

1. Go to this page and download the library: Download phphd/exceptional-validation library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

phphd / exceptional-validation example snippets


   PhPhD\ExceptionalMatcher\Bundle\PhdExceptionalMatcherBundle::class => ['all' => true],
   PhPhD\ExceptionToolkit\Bundle\PhdExceptionToolkitBundle::class => ['all' => true],
   

use PhPhD\ExceptionalMatcher\Rule\Object\Try_;
use PhPhD\ExceptionalMatcher\Rule\Object\Property\Catch_;

#[Try_]
class UserRegistration
{
    #[Catch_(LoginAlreadyTakenException::class)]
    public string $login;

    #[Catch_(PasswordCompromisedException::class)]
    public string $password;

    public function process(UserRegistrationServices $services): void
    {
        $userWithTheSameLogin = $services->userRepository->whereLogin($this->login)->firstOrNull();

        if (null !== $userWithTheSameLogin) {
            throw new LoginAlreadyTakenException($this->login);        
        }

        if ($services->passwordSecurity->isCompromised($this->password)) {
            throw new PasswordCompromisedException($this->password);
        }

        $services->entityManager->persist(new User($this->login, $this->password));
        $services->entityManager->flush();
    }
}

$registration = new UserRegistration('jzs', 'jn3.16');

$errors = [];
try {
    return $registration->process($services);
} catch (LoginAlreadyTakenException $e) {
    $errors['login'] = $e->getMessage();
} catch (PasswordCompromisedException $e) {
    $errors['password'] = $e->getMessage();
}

use PhPhD\ExceptionalMatcher\ExceptionMatcher;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Validator\ConstraintViolationListInterface;

#[AsController]
class RegisterUserApiPoint
{
    public function __construct(
        /** @var ExceptionMatcher<ConstraintViolationListInterface> */
        #[Autowire(service: ExceptionMatcher::class.'<'.ConstraintViolationListInterface::class.'>')]
        private ExceptionMatcher $matcher,
        private UserRegistrationServices $services,
    ) { }

    #[Route(
        path: '/register',
        name: 'user_register',
        methods: ['POST'],
    )]
    public function __invoke(
        #[MapRequestPayload] UserRegistration $registration,
    ): Response {
        try {
            $registration->process($this->services);
        } catch (Throwable $exception) {
            return $this->handleError($exception, $registration);
        }

        return new Response(status: HTTP_CREATED);
    }

    private function handleError(Throwable $exception, UserRegistration $registration): Response
    {
        /** @var ?ConstraintViolationListInterface $violationList */
        $violationList = $this->matcher->match($exception, $registration);

        if (null === $violationList) {
            throw $exception;
        }

        return new JsonResponse($violationList, HTTP_UNPROCESSABLE_ENTITY);   
    }
}

#[UniqueEntity(
    fields: ['login'],
    entityClass: User::class,
    identifierFieldNames: ['user' => 'id'],
)]
class UserProfileUpdate
{ 
    public string $login; 
@@

#[Try_]
class UserProfileUpdate
{ 
    #[Catch_(LoginAlreadyTakenException::class)]
    public string $login;
@@

use PhPhD\ExceptionalMatcher\Rule\Object\Try_;
use PhPhD\ExceptionalMatcher\Rule\Object\Property\Catch_;

#[Try_]
class UserProfileUpdate
{
    #[Catch_(LoginAlreadyTakenException::class)]
    public string $login;

    #[Catch_(PasswordCompromisedException::class)]
    #[Catch_(PasswordCannotBeReusedException::class)]
    public string $password;
    
    public function process(User $user, UserProfileUpdateServices $services): void
    {
        $userWithTheSameLogin = $services->userRepository->whereLogin($this->login)->firstOrNull();

        if ($userWithTheSameLogin?->is($user) === false) {
            throw new LoginAlreadyTakenException($this->login);
        }

        if ($services->passwordSecurity->isCompromised($this->password)) {
            throw new PasswordCompromisedException($this->password);
        }

        // Could throw PasswordCannotBeReusedException
        $user->updateProfile($this->login, $this->password);

        $services->entityManager->flush();
    }
}

use PhPhD\ExceptionalMatcher\Rule\Object\Property\Catch_;
use PhPhD\ExceptionalMatcher\Rule\Object\Try_;
use Symfony\Component\Uid\Exception\InvalidArgumentException as InvalidUidException;
use Symfony\Component\Validator\Exception\ValidationFailedException;

use const PhPhD\ExceptionalMatcher\Rule\Object\Property\Match\Condition\Enum\enum_value;
use const PhPhD\ExceptionalMatcher\Rule\Object\Property\Match\Condition\Uid\uid_value;
use const PhPhD\ExceptionalMatcher\Rule\Object\Property\Match\Condition\Value\exception_value;
use const PhPhD\ExceptionalMatcher\Rule\Object\Property\Match\Condition\Validator\validated_value;
use const PhPhD\ExceptionalMatcher\Validator\Formatter\Validator\validator_violations;

#[Try_]
class ImportProductDto
{
    #[Catch_(InvalidUidException::class, match: uid_value, message: 'This is not a valid UUID.')]
    public string $id;

    #[Catch_(CategoryNotFoundException::class, match: exception_value)] // Message is derived from Exception
    public string $categoryId;

    #[Catch_(\ValueError::class, from: ProductStatus::class, match: enum_value, message: 'The value you selected is not a valid choice.')]
    public string $status;

    #[Catch_(ValidationFailedException::class, from: ProductDescription::class, match: validated_value, format: validator_violations)]
    public string $description;

    #[Catch_(BackorderDisabledForCategoryException::class, if: [self::class, 'thisProductViolatesBackorder'])]
    public ?int $backorderLimit;

    /**
     * Needed in case of deep analysis.
     * 
     * If this method returns TRUE, the exception is linked to $backorderLimit of *this object*;
     * otherwise this exception has nothing to do with this object. 
     */
    public function thisProductViolatesBackorder(BackorderDisabledForCategoryException $exception): bool
    {
        if ($exception->categoryId !== $this->categoryId) {
            return false; // Backorder configuration of the given category has nothing to do with this category.
        }

        if (null === $this->backorderLimit) {
            return false; // The product didn't even enable backorder, much less violated it.
        }

        return true;
    }
}

use PhPhD\ExceptionalMatcher\Rule\Object\Try_;
use PhPhD\ExceptionalMatcher\Rule\Object\Property\Catch_;
use Symfony\Component\Validator\Constraints as Assert;

#[Try_]
class ImportProductBatchDto
{
    /** @var ImportProductDto[] */
    public array $items;
}

if ($exception->categoryId !== $this->categoryId) {
    // not my backpack
}

if (null === $this->backorderLimit) {
    // It's not my BackorderDisabledForCategoryException! I didn't enable backorder! 
}

use PhPhD\ExceptionalMatcher\Bundle\DependencyInjection\PhdExceptionalMatcherExtension;
use PhPhD\ExceptionalMatcher\ExceptionMatcher;
use PhPhD\ExceptionalMatcher\Exception\MatchedExceptionList;

$container = (new PhdExceptionalMatcherExtension())->getContainer([
    // These are not used but still 

use PhPhD\ExceptionalMatcher\Upgrade\ExceptionalMatcherSetList;

return RectorConfig::configure()
    ->withPaths([ __DIR__ . '/src'])
    ->withImportNames(removeUnusedImports: true)
    // Upgrading from your version (e.g. 1.4) to the latest version
    ->withSets(ExceptionalMatcherSetList::fromVersion('1.4')->getSetList());