version 1.1

This commit is contained in:
Maxime Renou 2021-07-07 10:26:43 +02:00
commit 85788e86b5
10 changed files with 860 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
vendor

2
ValidatorBundle/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
vendor
.idea

View File

@ -0,0 +1,24 @@
<?php
namespace Bluesquare\ValidatorBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Translation\TranslatorInterface;
class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$treeBuilder->root('validator');
return ($treeBuilder);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Bluesquare\ValidatorBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Contracts\Translation\TranslatorInterface;
class ValidatorExtension extends Extension
{
/**
* Loads a specific configuration.
*
* @throws \InvalidArgumentException When provided tag is not defined in this extension
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Ressources/config'));
$loader->load('services.yaml');
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
return $config;
}
public function getAlias()
{
return parent::getAlias(); // TODO: Change the autogenerated stub
}
}

21
ValidatorBundle/LICENSE Normal file
View File

@ -0,0 +1,21 @@
DON'T BE A DICK PUBLIC LICENSE
Version 1, December 2009
Copyright (C) 2009 Philip Sturgeon email@philsturgeon.co.uk
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
Do whatever you like with the original work, just don't be a dick.
Being a dick includes - but is not limited to - the following instances:
1a. Outright copyright infringement - Don't just copy this and change the name.
1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick.
1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick.
If you become rich through modifications, related works/services, or supporting the original work, share the love. Only a dick would make loads off this work and not buy the original works creator(s) a pint.
Code is provided with no warranty. Using somebody else's code and bitching when it goes wrong makes you a DONKEY dick. Fix the problem yourself. A non-dick would submit the fix back.

View File

@ -0,0 +1,7 @@
services:
bluesquare.validator:
class: Bluesquare\ValidatorBundle\Validator
autowire: true
public: true
arguments: ['@request_stack', '@translator']
Bluesquare\ValidatorBundle\Validator: '@bluesquare.validator'

View File

@ -0,0 +1,505 @@
<?php
namespace Bluesquare\ValidatorBundle;
use Bluesquare\StorageBundle\Exceptions\MimeTypeException;
use Bluesquare\StorageBundle\Storage;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class Validator
{
protected $request;
protected $context;
protected $errors;
protected $entity;
protected $values;
protected $rules;
protected $validated;
protected $translator;
public function __construct(RequestStack $requestStack, TranslatorInterface $translator)
{
$this->values = [];
$this->errors = [];
$this->rules = [];
$this->validated = false;
$this->translator = $translator;
$this->request = $requestStack->getCurrentRequest();
$session = $this->request->getSession()->getFlashBag()->get("Bluesquare:ValidatorBundle");
if(is_array($session) && isset($session[0])) {
if(isset($session[0]['context'])) $this->context = $session[0]['context'];
if(isset($session[0]['errors']) && is_array($session[0]['errors'])) $this->errors = $session[0]['errors'];
if(isset($session[0]['values']) && is_array($session[0]['values'])) $this->values = $session[0]['values'];
$this->validated = true;
}
if ($this->post())
{
$values = [];
foreach (array_merge($_GET, $_POST) as $field => $value) $values[$field] = $value;
$json = @json_decode(file_get_contents('php://input'), true);
if (is_array($json)) $values = array_merge($values, $json);
$this->values = array_merge($this->values, $values);
}
}
//
public function context($context)
{
$this->context = $context;
return $this;
}
public function entity($entity)
{
$this->entity = $entity;
return $this;
}
public function set($key, $value)
{
$this->values[$key] = $value;
return $this;
}
public function error($field, $error)
{
if (!isset($this->errors[$field]))
$this->errors[$field] = [];
$this->errors[$field][] = $error;
return $this;
}
//
public function json($code = 400, $data = [])
{
$errors = [];
foreach ($this->errors as $field => $field_errors)
{
$errors[$field] = [
'error' => "validator.".$field_errors[0].':'.$field.(!is_null($this->context) ? ':'.$this->context : '')
];
}
$data = array_merge([
'message' => 'Please check your input',
'error' => "validator.form-error".(!is_null($this->context) ? ':'.$this->context : ''),
'errors' => $errors
], $data);
return new JsonResponse($data, $code);
}
public function keep()
{
$data = [
'errors' => $this->errors,
'values' => $this->values,
'context' => $this->context
];
$this->request->getSession()->getFlashBag()->add('Bluesquare:ValidatorBundle', $data);
return $this;
}
public function validated()
{
return $this->validated;
}
public function failed()
{
return (count($this->errors) > 0);
}
public function errors()
{
$translator = $this->translator;
$context = $this->context;
return array_map(function ($errors) use ($translator, $context) {
$error = $errors[0];
$message = null;
if (!is_null($context)) {
$message = $translator->trans("$context.$error", [], "validator");
}
if (is_null($message) || $message == "$context.$error") {
$message = $translator->trans("$error", [], "validator");
}
return $message;
}, $this->errors);
}
public function message()
{
$result = $this->failed() ? 'form_error' : 'form_success';
$message = null;
if (!is_null($this->context)) {
$message = $this->translator->trans("$this->context.$result", [], "validator");
}
if (is_null($message) || $message == "$this->context.$result") {
$message = $this->translator->trans("$result", [], "validator");
}
return $message;
}
public function has($field)
{
return !is_null($this->get($field));
}
public function value($field, $default = null)
{
$value = isset($this->values[$field]) ? trim($this->values[$field]) : $default;
if (empty($value)) $value = $default;
if (is_null($value) && !is_null($this->entity)) {
$method = "get".$this->camelize($field);
if (method_exists($this->entity, $method))
$value = $this->entity->$method();
}
return $value;
}
public function get($field, $default = null)
{
$value = isset($this->values[$field]) ? trim($this->values[$field]) : $default;
if (strlen($value) == 0) $value = $default;
return $value;
}
public function checked($field)
{
return $this->post() ? (
!is_null($this->get($field))
) : (
!is_null($this->value($field)) && $this->value($field) != 0 && $this->value($field) != false
);
}
public function isChecked($field)
{
return $this->has($field);
}
public function getFile($name)
{
return $this->request->files->get($name);
}
public function hasFile($name)
{
$file = $this->getFile($name);
return !is_null($file) && !(is_array($file) || $file instanceof Traversable);
}
public function hasFiles($name)
{
$files = $this->getFile($name);
return !is_null($files) && (is_array($files) || $files instanceof Traversable);
}
public function inject()
{
$args = func_get_args();
if (count($args) === 0) return false;
if (is_object($args[0])) $entity = array_shift($args);
else $entity = $this->entity;
if (is_null($entity)) return false;
foreach ($args as $field) {
$method = "set".$this->camelize($field);
if (method_exists($entity, $method)) $entity->$method($this->get($field));
}
return true;
}
public function injectFile()
{
$args = func_get_args();
if (count($args) === 0) return false;
if (is_object($args[0])) $entity = array_shift($args);
else $entity = $this->entity;
if (is_null($entity) || count($args) != 2) return false;
$attribute = $args[0];
$storage = $args[1];
if (!$this->hasFile($attribute))
{
$this->error($attribute, 'required');
return false;
}
if ($storage instanceof Storage)
{
try {
return $storage->store($entity, $attribute, $this->getFile($attribute));
}
catch(MimeTypeException $exception) {
$this->error($attribute, 'file_mime_type');
}
}
return false;
}
//
public function post()
{
return in_array(strtolower($this->request->getMethod()), ['delete', 'put', 'post', 'patch']);
}
public function check()
{
return $this->validate();
}
//
protected function validate()
{
foreach ($this->rules as $field => $rules)
{
$rules_names = array_map(function($rule) { return $rule['rule']; }, $rules);
$nullable = !in_array('required', $rules_names) && !in_array('required_file', $rules_names) && !in_array('required_files', $rules_names);
foreach ($rules as $rule) $this->test($field, $rule, $nullable);
}
$this->validated = true;
return (count($this->errors) == 0);
}
public function test($field, $rule, $nullable = false)
{
$name = $rule['rule'];
$data = $rule['data'];
$value = $this->get($field);
$success = true;
switch ($name)
{
case 'required_file':
$success = $this->hasFile($field);
break;
case 'required_files':
$success = $this->hasFiles($field);
break;
case 'required':
$success = !(is_null($value));
break;
case 'integer':
$success = (filter_var($value, FILTER_VALIDATE_INT) !== false);
break;
case 'float':
$success = (filter_var($value, FILTER_VALIDATE_FLOAT) !== false);
break;
case 'boolean':
$success = (filter_var($value, FILTER_VALIDATE_BOOLEAN) !== false);
break;
case 'email':
$success = (filter_var($value, FILTER_VALIDATE_EMAIL) !== false);
break;
case 'phone':
$_pattern = "/^\+?\d{7,15}$/";
$success = (!(strlen($value) == 10 && ctype_digit($value)) && !preg_match($_pattern, $value)) ? false : true;
break;
case 'zipcode':
$success = (!(strlen($value) == 5 && ctype_digit($value))) ? false : true;
break;
case 'alphanumeric':
$success = ctype_alnum($value);
break;
case 'date':
$success = preg_match('/^([0-9]{2}\/[0-9]{2}\/[0-9]{4})|([0-9]{4}-[0-9]{2}-[0-9]{2})$/', $value);
break;
case 'datetime':
$_pattern = "[0-9]{4}\-[0-9]{2}\-[0-9]{2}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}";
$success = (preg_match($_pattern, $value)) ? true : false;
break;
case 'url':
$_pattern = "%^((?:(?:https?|ftp)://))?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$%iuS";
$success = preg_match($_pattern, $value);
break;
case 'in_array':
$success = in_array($value, $data['values']);
break;
case 'min':
$success = min($value, $data['length']) == $value ? true : false;
break;
case 'max':
$success = max($value, $data['length']) == $value ? true : false;
break;
case 'min_length':
$success = strlen($value) >= $data['length'] ? true : false;
break;
case 'max_length':
$success = strlen($value) <= $data['length'] ? true : false;
break;
case 'identical':
$success = $value == $this->get($data['target']);
break;
// (éwé c'est un switch)
}
if (!$success)
{
if ($nullable && empty($value))
return true;
$this->error($field, $name);
}
return $success;
}
public function rule($field, $rule, $data = [])
{
if (!isset($this->rules[$field]))
$this->rules[$field] = [];
$this->rules[$field][] = ['rule' => $rule, 'data' => $data];
return $this;
}
// Rules: multiple
public function required()
{
foreach (func_get_args() as $field) $this->rule($field, 'required');
return $this;
}
public function integer()
{
foreach (func_get_args() as $field) $this->rule($field, 'integer');
return $this;
}
public function float()
{
foreach (func_get_args() as $field) $this->rule($field, 'float');
return $this;
}
public function boolean()
{
foreach (func_get_args() as $field) $this->rule($field, 'boolean');
return $this;
}
public function email()
{
foreach (func_get_args() as $field) $this->rule($field, 'email');
return $this;
}
public function phone()
{
foreach (func_get_args() as $field) $this->rule($field, 'phone');
return $this;
}
public function zipcode()
{
foreach (func_get_args() as $field) $this->rule($field, 'zipcode');
return $this;
}
public function alphanumeric()
{
foreach (func_get_args() as $field) $this->rule($field, 'alphanumeric');
return $this;
}
public function date()
{
foreach (func_get_args() as $field) $this->rule($field, 'date');
return $this;
}
public function datetime()
{
foreach (func_get_args() as $field) $this->rule($field, 'datetime');
return $this;
}
public function url()
{
foreach (func_get_args() as $field) $this->rule($field, 'url');
return $this;
}
// Rules: single
public function requiredFile($name)
{
$this->rule($name, 'required_file');
return $this;
}
public function requiredFiles($name)
{
$this->rule($name, 'required_files');
return $this;
}
public function min($field, $length)
{
$this->rule($field, 'min', ['length' => $length]);
return $this;
}
public function max($field, $length)
{
$this->rule($field, 'max', ['length' => $length]);
return $this;
}
public function minLength($field, $length)
{
$this->rule($field, 'min_length', ['length' => $length]);
return $this;
}
public function maxLength($field, $length)
{
$this->rule($field, 'max_length', ['length' => $length]);
return $this;
}
public function inArray($field, $values)
{
$this->rule($field, 'in_array', ['values' => $values]);
return $this;
}
public function identical($field, $field_confirm)
{
$this->rule($field_confirm, 'identical', ['target' => $field]);
return $this;
}
// Helpers
protected function camelize($string)
{
$string = implode('_', explode('-', $string));
$words = array_map('ucfirst', explode('_', $string));
return implode('', $words);
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Bluesquare\ValidatorBundle;
use Bluesquare\ValidatorBundle\DependencyInjection\ValidatorExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ValidatorBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
}
public function getContainerExtension()
{
if (null === $this->extension)
$this->extension = new ValidatorExtension();
return $this->extension;
}
}

23
composer.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "bluesquare-packages/symfony-validator",
"description": "Validator created by Bluesquare Computing",
"keywords": ["template", "composer", "package"],
"license": "proprietary",
"authors": [
{
"name": "RENOU Maxime",
"email": "maxime@bluesquare.io"
}
],
"type": "symfony-bundle",
"require": {
"php": ">=7.1",
"symfony/translation": "^4.2",
"bluesquare-packages/symfony-storage": "dev-master"
},
"autoload": {
"psr-4": {
"Bluesquare\\ValidatorBundle\\": "ValidatorBundle/"
}
}
}

220
composer.lock generated Normal file
View File

@ -0,0 +1,220 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2c11f55a0316234fd9227351ee9d9a67",
"packages": [
{
"name": "symfony/contracts",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/contracts.git",
"reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf",
"reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf",
"shasum": ""
},
"require": {
"php": "^7.1.3"
},
"require-dev": {
"psr/cache": "^1.0",
"psr/container": "^1.0"
},
"suggest": {
"psr/cache": "When using the Cache contracts",
"psr/container": "When using the Service contracts",
"symfony/cache-contracts-implementation": "",
"symfony/service-contracts-implementation": "",
"symfony/translation-contracts-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\": ""
},
"exclude-from-classmap": [
"**/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A set of abstractions extracted out of the Symfony components",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"time": "2018-12-05T08:06:11+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "c79c051f5b3a46be09205c73b80b346e4153e494"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494",
"reference": "c79c051f5b3a46be09205c73b80b346e4153e494",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2018-09-21T13:07:52+00:00"
},
{
"name": "symfony/translation",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "23fd7aac70d99a17a8e6473a41fec8fab3331050"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/23fd7aac70d99a17a8e6473a41fec8fab3331050",
"reference": "23fd7aac70d99a17a8e6473a41fec8fab3331050",
"shasum": ""
},
"require": {
"php": "^7.1.3",
"symfony/contracts": "^1.0.2",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/config": "<3.4",
"symfony/dependency-injection": "<3.4",
"symfony/yaml": "<3.4"
},
"provide": {
"symfony/translation-contracts-implementation": "1.0"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~3.4|~4.0",
"symfony/console": "~3.4|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/finder": "~2.8|~3.0|~4.0",
"symfony/intl": "~3.4|~4.0",
"symfony/yaml": "~3.4|~4.0"
},
"suggest": {
"psr/log-implementation": "To use logging capability in translator",
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.2-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Translation\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2019-01-27T23:11:39+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.1"
},
"platform-dev": []
}