xsolve-pl/face-validator-bundle
A Symfony bundle from Xsolve for face validation in uploaded images. It integrates face-detection checks into your application so you can reject photos without a detectable human face, ideal for profile pictures, identity verification, and user onboarding.
This is a small Symfony 3 bundle that wraps Microsoft Azure Face API’s face-detection endpoint. Its likely day-to-day value is narrow: given an uploaded image, ask Azure whether a face is present (and possibly whether exactly one face is present) before accepting it as an avatar/profile photo. It is not a face-recognition, identity-verification, or liveness-detection library.
If you are a Laravel developer, the first thing to note is that this bundle is not directly usable in Laravel. It is a Symfony bundle with Symfony 3-era service/configuration conventions. Treat it as a reference implementation for the Azure Face API call, or port the relevant service into a Laravel package/service class. For a Symfony 3 application, the minimal path is:
Require the package:
composer require xsolve-pl/face-validator-bundle
Register the bundle in app/AppKernel.php:
$bundles = [
// ...
new Xsolve\FaceValidatorBundle\XsolveFaceValidatorBundle(),
];
Create an Azure Face API resource and note the subscription key and regional endpoint, e.g. https://westeurope.api.cognitive.microsoft.com.
Inspect the actual configuration keys rather than guessing:
bin/console config:dump-reference xsolve_face_validator
bin/console debug:container | grep -i face
The bundle is old and lightly documented, so DependencyInjection/Configuration.php and Resources/config/services.yml are the authoritative sources.
Configure it approximately like this, adjusting keys to match the dumped reference:
# app/config/config.yml
xsolve_face_validator:
azure:
subscription_key: "%azure_face_key%"
endpoint: "https://westeurope.api.cognitive.microsoft.com"
First use case: validate an avatar upload in a controller or upload service.
$validator = $this->get('xsolve_face_validator.validator'); // verify actual service ID
$result = $validator->validate($uploadedFile->getPathname());
if (!$result) {
throw new \RuntimeException('The image does not contain a detectable face.');
}
If the bundle exposes a Symfony Validator constraint, prefer that over manual controller calls. Check the Validator/ directory and the README for a constraint class; if one exists, attach it to the FileType field:
$builder->add('avatar', FileType::class, [
'constraints' => [
new \Xsolve\FaceValidatorBundle\Validator\Constraints\Face(),
],
]);
Wrap the bundle behind your own service. Since the package is stale and has very low adoption, avoid scattering its service ID or result object throughout your code. Create an application-level AvatarValidator that depends on the bundle service, translates exceptions into domain results, and makes it easy to swap the implementation later.
final class AvatarValidator
{
public function __construct(private FaceValidator $faceValidator) {}
public function isValid(string $path): bool
{
try {
return $this->faceValidator->validate($path);
} catch (\Throwable $e) {
// Decide whether API failure should block upload or be queued for retry.
throw new AvatarValidationFailed('Face validation unavailable', 0, $e);
}
}
}
Use it in the upload workflow, not at render time. Validate during upload or profile update, store the result as a flag on your media entity, and avoid calling Azure every time the image is displayed. A typical flow is: receive UploadedFile, move it to temporary storage, call the validator, delete the temp file, persist the image only if valid.
Prefer a custom Symfony constraint if the bundle does not provide one. This keeps validation declarative and testable.
final class ContainsFace extends Constraint {}
final class ContainsFaceValidator extends ConstraintValidator
{
public function __construct(private AvatarValidator $avatarValidator) {}
public function validate($value, Constraint $constraint): void
{
if (!$value instanceof UploadedFile || !$value->isValid()) {
return;
}
if (!$this->avatarValidator->isValid($value->getPathname())) {
$this->context->buildViolation('No face was detected in this image.')
->addViolation();
}
}
}
Decide your validation policy explicitly. “Validation” can mean different things:
Azure’s detection response includes face rectangles and attributes depending on query parameters. If the bundle only returns a boolean, you may need to fork or extend it to inspect the number of faces and bounding boxes.
Use asynchronous validation for large uploads. Azure calls add latency and can fail. For a user-facing upload form, consider accepting the file, storing it as pending, dispatching a queued job, and updating the status later. This is especially relevant if your app handles many uploads or is used in regions far from the Azure endpoint.
Mock Azure in tests. Do not hit the real Face API in CI. If the bundle uses Guzzle internally, replace the HTTP client with a mock handler or decorate the bundle’s client service. Add fixtures for: valid face image, no-face image, multiple-face image, Azure 400/403/429 response, timeout.
Keep Azure credentials out of code. Use Symfony parameters and environment variables. In Symfony 3, %env(AZURE_FACE_KEY)% requires a sufficiently recent 3.x release; otherwise use parameters.yml or a secrets mechanism.
The package is effectively abandoned. Last release was January 2018, it targets Symfony 3, and it has essentially no dependents or community activity. Expect incompatibility with Symfony 4/5/6, modern PHP versions, and modern Azure SDKs. Before adopting, check composer.json constraints and run a small proof of concept. In many cases, copying the Azure request logic into a small internal service is safer than depending on the bundle.
Azure Face API has changed since 2018. Endpoint versions, available attributes, pricing, and quota behavior may differ from what the bundle assumes. The old bundle likely calls the face/v1.0/detect endpoint directly. Verify that your Azure resource type and region still support that endpoint, and confirm current request limits and image constraints in Azure documentation.
“Face validation” is not identity verification. Detection only tells you a face-like region exists. It does not prove the person is who they claim to be, does not prove liveness, and should not be used alone for KYC, age verification, or anti-impersonation. If you need those, use Azure Face verify/identify with explicit consent and a proper compliance review.
Handle “no face” differently from “API failed.” A valid HTTP 200 response with an empty face array means the image is invalid for your use case. A 401, 403, 429, timeout, or malformed response means your system could not validate. Do not silently reject users because Azure is down; either queue a retry or allow manual review.
Watch image constraints and preprocessing. Azure detection has minimum and maximum image dimensions and file sizes. Uploaded phone photos can be large; avatars can be tiny. Resize or reject before calling the API. Also normalize EXIF orientation, because a rotated photo may still detect but bounding boxes may surprise you.
Rate limits and cost are real. A public avatar upload endpoint can be abused. Add your own file-size limits, MIME sniffing, authentication, and per-user throttling before spending Azure quota. Cache validation results per image hash if the same file can be submitted repeatedly.
Configuration quirks are likely. The bundle is old enough that service autowiring, bundle flex recipes, and modern config trees will not exist. Use config:dump-reference, debug:container, and debug:config to discover the real keys and service IDs. If the service is not public, inject it into your own service rather than fetching it from the container.
Extension points are limited but useful. Typical escape hatches are: override the bundle’s HTTP client service, decorate the validator service, or fork the bundle and replace the Azure call. If you need face count, bounding boxes, or attributes, inspect the response DTO; if it is not exposed, forking is usually simpler than fighting the abstraction.
Legal and privacy review is mandatory. Face data is biometric data in many jurisdictions. Do not store raw images or Azure face IDs longer than necessary, disclose processing, obtain consent where required, and check whether your Azure region and contract satisfy GDPR or other applicable rules. For a simple avatar check, consider whether a non-biometric image moderation service would meet the requirement with less risk.
How can I help you explore Laravel packages today?