PHP code example of makraz / verify-email-change-bundle
1. Go to this page and download the library: Download makraz/verify-email-change-bundle 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/ */
makraz / verify-email-change-bundle example snippets
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Makraz\Bundle\VerifyEmailChange\Model\EmailChangeableInterface;
#[ORM\Entity]
class User implements EmailChangeableInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private string $email;
// ... rest of your entity
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
}
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Attribute\Route;
use Makraz\Bundle\VerifyEmailChange\EmailChange\EmailChangeHelper;
use Makraz\Bundle\VerifyEmailChange\Exception\VerifyEmailChangeExceptionInterface;
use Doctrine\ORM\EntityManagerInterface;
class EmailChangeController extends AbstractController
{
public function __construct(
private readonly EmailChangeHelper $emailChangeHelper,
private readonly EntityManagerInterface $entityManager,
private readonly MailerInterface $mailer,
) {}
#[Route('/account/email/change', name: 'app_email_change_request')]
public function request(Request $request): Response
{
/** @var User $user */
$user = $this->getUser();
// Your form handling here...
$newEmail = $request->request->get('new_email');
try {
// Generate the verification signature
$signature = $this->emailChangeHelper->generateSignature(
'app_email_change_verify',
$user,
$newEmail
);
$this->entityManager->flush();
// Send verification email to the NEW address
$email = (new Email())
->to($newEmail)
->subject('Verify your new email address')
->html(sprintf(
'Click here to verify: <a href="%s">%s</a><br>This link will expire in 1 hour.',
$signature->getSignedUrl(),
$signature->getSignedUrl()
));
$this->mailer->send($email);
$this->addFlash('success', 'Verification email sent! Check your new inbox.');
} catch (VerifyEmailChangeExceptionInterface $e) {
$this->addFlash('error', $e->getReason());
}
return $this->redirectToRoute('app_profile');
}
#[Route('/account/email/verify', name: 'app_email_change_verify')]
public function verify(Request $request): Response
{
try {
$user = $this->emailChangeHelper->validateTokenAndFetchUser($request);
$oldEmail = $this->emailChangeHelper->confirmEmailChange($user);
$this->entityManager->flush();
$this->addFlash('success', 'Email changed successfully!');
} catch (VerifyEmailChangeExceptionInterface $e) {
$this->addFlash('error', $e->getReason());
return $this->redirectToRoute('app_email_change_request');
}
return $this->redirectToRoute('app_profile');
}
#[Route('/account/email/cancel', name: 'app_email_change_cancel', methods: ['POST'])]
public function cancel(): Response
{
/** @var User $user */
$user = $this->getUser();
$this->emailChangeHelper->cancelEmailChange($user);
$this->entityManager->flush();
$this->addFlash('success', 'Email change cancelled.');
return $this->redirectToRoute('app_profile');
}
}
public function generateSignature(
string $routeName,
EmailChangeableInterface $user,
string $newEmail,
array $extraParams = []
): EmailChangeSignature
public function validateTokenAndFetchUser(Request $request): EmailChangeableInterface
public function confirmEmailChange(EmailChangeableInterface $user): string
public function cancelEmailChange(EmailChangeableInterface $user): void
public function hasPendingEmailChange(EmailChangeableInterface $user): bool
public function getPendingEmail(EmailChangeableInterface $user): ?string
use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
#[AsPeriodicTask('1 day')]
class PurgeExpiredEmailChangeRequestsMessage
{
// This message triggers the purge command
}
use Makraz\Bundle\VerifyEmailChange\Event\EmailChangeInitiatedEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener]
class EmailChangeListener
{
public function __invoke(EmailChangeInitiatedEvent $event): void
{
$user = $event->getUser();
$newEmail = $event->getNewEmail();
$oldEmail = $event->getOldEmail();
$verificationUrl = $event->getVerificationUrl();
}
}
$signature = $this->emailChangeHelper->generateSignature(
'app_email_change_verify',
$user,
$newEmail
);
// Send verification to the NEW email address
$this->sendEmail($newEmail, $signature->getSignedUrl());
// Also send confirmation to the OLD email address
if ($signature instanceof EmailChangeDualSignature) {
$this->sendEmail($user->getEmail(), $signature->getOldEmailSignedUrl());
}
#[Route('/account/email/verify-old', name: 'app_email_change_verify_old')]
public function verifyOldEmail(Request $request): Response
{
try {
$user = $this->emailChangeHelper->validateOldEmailToken($request);
// Check if both confirmations are done
$oldEmail = $this->emailChangeHelper->confirmEmailChange($user);
$this->entityManager->flush();
$this->addFlash('success', 'Email changed successfully!');
} catch (VerifyEmailChangeExceptionInterface $e) {
$this->addFlash('info', $e->getReason());
}
return $this->redirectToRoute('app_profile');
}
use Makraz\Bundle\VerifyEmailChange\Security\CsrfTokenHelper;
class EmailChangeController extends AbstractController
{
public function __construct(
private readonly EmailChangeHelper $emailChangeHelper,
private readonly CsrfTokenHelper $csrfHelper,
) {}
#[Route('/account/email/cancel', name: 'app_email_change_cancel', methods: ['POST'])]
public function cancel(Request $request): Response
{
if (!$this->csrfHelper->isTokenValid($request)) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
$this->emailChangeHelper->cancelEmailChange($this->getUser());
$this->addFlash('success', 'Email change cancelled.');
return $this->redirectToRoute('app_profile');
}
}
use Makraz\Bundle\VerifyEmailChange\EmailChange\EmailChangeHelper;
use Makraz\Bundle\VerifyEmailChange\Notifier\EmailChangeNotifier;
class EmailChangeController extends AbstractController
{
public function __construct(
private readonly EmailChangeHelper $emailChangeHelper,
private readonly EmailChangeNotifier $notifier,
) {}
public function request(Request $request): Response
{
$user = $this->getUser();
$newEmail = $request->request->get('new_email');
$signature = $this->emailChangeHelper->generateSignature(
'app_email_change_verify',
$user,
$newEmail
);
// Sends verification to new email (and old email in dual mode)
$this->notifier->sendVerificationEmail($user, $newEmail, $signature);
return $this->redirectToRoute('app_profile');
}
public function verify(Request $request): Response
{
$user = $this->emailChangeHelper->validateTokenAndFetchUser($request);
$oldEmail = $this->emailChangeHelper->confirmEmailChange($user);
// Notify old email address about the change
$this->notifier->sendEmailChangeConfirmation($user, $oldEmail, $user->getEmail());
return $this->redirectToRoute('app_profile');
}
}
use Makraz\Bundle\VerifyEmailChange\Api\EmailChangeResponseFactory;
use Makraz\Bundle\VerifyEmailChange\Exception\VerifyEmailChangeExceptionInterface;
class ApiEmailChangeController
{
public function __construct(
private readonly EmailChangeHelper $emailChangeHelper,
private readonly EmailChangeResponseFactory $responseFactory,
) {}
#[Route('/api/email/change', methods: ['POST'])]
public function request(Request $request): JsonResponse
{
try {
$user = $this->getUser();
$newEmail = $request->toArray()['new_email'];
$signature = $this->emailChangeHelper->generateSignature(
'api_email_change_verify', $user, $newEmail
);
return $this->responseFactory->initiated($newEmail, $signature->getExpiresAt());
} catch (VerifyEmailChangeExceptionInterface $e) {
return $this->responseFactory->error($e);
}
}
#[Route('/api/email/status', methods: ['GET'])]
public function status(): JsonResponse
{
$user = $this->getUser();
return $this->responseFactory->pendingStatus(
$this->emailChangeHelper->getPendingEmail($user)
);
}
}
use Makraz\Bundle\VerifyEmailChange\Otp\OtpEmailChangeHelper;
class OtpEmailChangeController
{
public function __construct(
private readonly OtpEmailChangeHelper $otpHelper,
) {}
#[Route('/api/email/otp/request', methods: ['POST'])]
public function request(Request $request): JsonResponse
{
$user = $this->getUser();
$newEmail = $request->toArray()['new_email'];
$result = $this->otpHelper->generateOtp($user, $newEmail);
// Send the OTP to the new email address
// $this->mailer->send(...$result->getOtp()...)
return new JsonResponse([
'message' => 'OTP sent to new email address.',
'expires_at' => $result->getExpiresAt()->format(\DateTimeInterface::ATOM),
]);
}
#[Route('/api/email/otp/verify', methods: ['POST'])]
public function verify(Request $request): JsonResponse
{
$user = $this->getUser();
$otp = $request->toArray()['otp'];
$oldEmail = $this->otpHelper->verifyOtp($user, $otp);
return new JsonResponse([
'message' => 'Email changed successfully.',
'old_email' => $oldEmail,
'new_email' => $user->getEmail(),
]);
}
}
use Makraz\Bundle\VerifyEmailChange\Event\EmailChangeAuditEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener]
class EmailChangeAuditListener
{
public function __invoke(EmailChangeAuditEvent $event): void
{
$action = $event->getAction(); // 'initiated', 'confirmed', 'failed_verification', etc.
$user = $event->getUser();
$ip = $event->getIpAddress();
$metadata = $event->getMetadata();
// Log to your audit system
}
}
use Makraz\Bundle\VerifyEmailChange\Exception\VerifyEmailChangeExceptionInterface;
try {
// Your email change logic
} catch (VerifyEmailChangeExceptionInterface $e) {
$this->addFlash('error', $e->getReason());
}