auraphp / aura.filter Goto Github PK
View Code? Open in Web Editor NEWValidate and sanitize arrays and objects.
License: MIT License
Validate and sanitize arrays and objects.
License: MIT License
Hey,
A great package, but wondered what the timeline is for supporting php 7? There is a class Bool
which is now a reserved keyword in php7.
Hi all,
I would like to discuss if anyone like this idea.
Currently the way of creating the Filter is via a FilterFactory and call the newSubjectFilter
.
What I feel is we could improve the usage with something like
$filter = new Aura\Filter\SubjectFilter();
and in the class it can automatically add the dependencies if not passed.
class SubjectFilter
{
public function __construct(
ValidateSpec $validate_spec = null,
SanitizeSpec $sanitize_spec = null,
FailureCollection $failures = null
) {
$this->validate_spec = $validate_spec ?: new ValidateSpec(new ValidateLocator());
$this->sanitize_spec = $sanitize_spec ?: new SanitizeSpec(new SanitizeLocator());
$this->proto_failures = $failures ?: new FailureCollection();
$this->init();
}
}
This helps to use Di
if they are using it and if not passing their own or alternatively without anything if there is no other custom rule dependency.
I can make a PR if the idea seems good.
Thank you.
Often I wish to set a field (ie. slug
) to the value of another field (ie. title) if the former is blank, then sanitize it further. I think I can accomplish this now with some magic strings like this:
$filter->sanitize('slug')->useBlankValue('FIELD_IS_BLANK');
$filter->sanitize('slug')->to('callback', function ($subject, $field) {
if ($subject->$field == 'FIELD_IS_BLANK') {
$subject->$field = $subject->title;
}
return true;
});
$filter->sanitize('slug')->to('uppercase');
$data = [
'slug' => '',
'title' => 'some thing here'
];
var_dump($filter->apply($data));
var_dump($data);
But that's kind of awkward/magical/weird.
Any suggestions? Think we need a PR?
I was thinking about patching the SanatizeSpec
to be able to pass a callable
to useBlankValue
that would take the subject as an arg, but i don't actually think that's the way.
ie.:
// SanitizeSpec::__invoke
public function __invoke($subject)
{
if (! $this->subjectFieldIsBlank($subject)) {
return parent::__invoke($subject);
}
if (! $this->allow_blank) {
return false;
}
$blank = $this->blank_value;
if (is_callable($blank)) {
$blank = $blank($subject, $this->field);
}
$field = $this->field;
$subject->$field = $blank;
return true;
}
// filter
$filter->sanitize('slug')->useBlankValue(
function ($subject, $field) {
return $subject->title;
}
);
Maybe what I want isn't really a "blank" value, but a "defaultTo" or something? and should encompass a new set of logic?
// SanitizeSpec::__invoke
public function __invoke($subject)
{
if (! $this->subjectFieldIsBlank($subject)) {
return parent::__invoke($subject);
}
if ($defaultField = $this->defaultTo) {
$subject->$field = $subject->$default;
return parent::__invoke($subject);
}
// ...
}
Thoughts?
$filter->validate('field')->is('upload');
Using this, with a PSR based request, how do I test the uploadedFiles part of the request?
When working with Aura.Intl how could we get the tokens that need to be passed .
Eg :
"FILTER_RULE_FAILURE_IS_NOT_BETWEEN" => "Please do not use a value between {min} and {max}.",
The min
and max
, may be something like getTokens()
on the RuleCollection::addMessageFromRule
will help ?
array(
'min' => $min,
'max' => $max,
);
/ cc @cordoval
We are currently allowing users to validate by using a string value to indicate the validator they wish to use:
$filter->validate('credit_card')->is('creditCard');
For built-in validators, we should instead use constants:
$filter->validate('credit_card')->is(Filter::CREDIT_CARD);
PHP 7.2 is reporting idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated.
src: src/Rule/Validate/Email.php:413
Are there any plans to update this - I could do a pull request?
I have a set of rules that I have configured:
$filter->validate('first_name')->isNotBlank();
$filter->validate('last_name')->isNotBlank();
$filter->validate('email')->is('email');
$filter->validate('cell_number')->isNotBlank();
$filter->validate('cell_number')->is('phoneNumber')->asStopRule();
$filter->validate('password')->isNotBlank();
When I pass an invalid phone number, and a blank password, I expect that the phone number stop rule will prevent processing of the password check. This does not happen. I'm using 2.3.1.
Say I have a $subject like this:
$subject = [
'field' => 'value'
'array_field' => [
1,
2
]
];
What is the best way to create a $filter that will ensure that field
is a string and that array_field
only contains integers?
Currently I'm using a custom callback function. Is there a better way?
Hey @jakejohns, can you update docs/validate.md and docs/sanitize.md to include your new character case rules?
This might be 'too particular a use case', but I often find myself "sluggifying"
strings or checking strings for "slugginess".
Is there space for something like this?
I didn't submit a PR, since I thought I'd see what people thought before wasting
too much time. (Also, not sure I know the best way to deal with various out of
bound characters)
Alternatively, it may be better for me to just write something to integrate
aura/filter with some existing solution.
Most implementations of stuff like this are much more robust that what I'm
including as basic illustration of concept. Most handle things like transcoding
certain characters, take some amount of configuration, etc.
namespace Aura\Filter\Rule\Validate;
class Alnum
{
public function __invoke($subject, $field)
{
$value = $subject->$field;
if (! is_scalar($value)) {
return false;
}
return preg_match('/^[a-z][-a-z0-9]*$/', $value);
}
}
namespace Aura\Filter\Rule\Sanitize;
class Slug
{
public function __invoke($subject, $field)
{
$value = $subject->$field;
if (! is_scalar($value)) {
return false;
}
$value = preg_replace('/[^a-z0-9-]+/', '-', strtolower($value));
$value = preg_replace("/-+/", '-', $value);
$value = trim($value, '-');
$subject->$field = $value;
return true;
}
}
Thoughts?
In the readme you have the the following:
(Note that if we instantiate manually, we will need to configure the RuleLocator manually to add rule services. See the "Advanced Usage" section near the end of this page for more information.)
This section doesn't seem to be present?
The closure filtering is typehinting using Closure, this means that callable methods or functions from elsewhere cannot be used
Using the Callable typehint instead (available since 5.4) would allow any legal callable to be used instead (and would require no code changes except the typehint)
Why not a single locator for both SanitizeLocator
and ValidateLocator
has no special methods needed.
How would you recommend configuring the dependency injection container in the Aura.Di package for use with Aura.Filter's RuleCollection and RuleLocator classes? I've been trying to work it out for a few days and keep getting stuck. Are you able to provide an example?
What is the future of this project? In my opinion, it is feature complete and only needs to have the latest versions of PHP (7.3, 7.4 & 8.0 as of this writing) tested via travis or Github Actions to ensure it continues to work with the currently supported version of PHP. Will anyone be updating the CI tests for this project?
My expectation is that isNotBlank
would give me an error message if the passed in field was blank.
When I pass a field in that has a value, this validation returns an error.
Currently, it's acceptable to have numbers in the top level domain. Which should only be used for internationalized domains(starting with xn-- as stated in https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains
Ex. [email protected] should be invalid, but it's not.
Com1 doesn't start with xn and is not an internationalized domain either.
It's okay to be able to use an IP instead of a domain name(ex. [email protected]), but not a mix of both.
Also, as far as I know domains must be at least 2 symbols long(did a search). Hence:
[email protected] should be invalid as well.
Another thing is - what about strong email validation using https://secure.php.net/manual/en/function.checkdnsrr.php
Since it makes a network call, maybe it should be in another rule, EmailMX or something.
Hi, my name is Satomi. I'm using aurafilter, but upon doing composer update
, I updated from "2.0.0-beta2" to "2.0.0", and it seems like the "blank" validation rule went away. I'm getting this error: Aura\Filter\Exception\RuleNotMapped: blank
. Did the name of this validation rule change? Thank very much!
1 ) composer.json of master is wrong one . Please update with that of the recent develop, so we can submit to packagist .
2 ) Now showing forked from pmjones .
Have you submitted to github guys a ticket or we can make it as we did for Aura.Uri ?
I was looking why aura/filter is not getting installed in packagist or is not easily reachable to people when newer components get installed more.
The tag validation is not in master. The master have the tag, we need to push it or edit the composer.json of master to have the validation tag. People are not looking for filters.
Hi Paul,
I do understand that the v1 is EOL. But as v2 is not yet released also can you make one last release with the fix having the set message on the Translator.php ?
README.md says
(Note that if we instantiate it manually, we will need to configure the RuleLocator manually to add rule services. See the "Advanced Usage" section near the end of this page for more information.)
But there is no "Advanced Usage" section in README.
I've just come across a great scenario where I need to get all of the failure data as json for a vue.js component to take over.
Would it be a good idea to implement JsonSerializable
in the Failure class?
I was looking at the code and found that currently the non-mb string functions are being used:
ex:
Aura\Filter\Rule\Sanitize\Strlen
if (strlen($value) < $len) {
$subject->$field = str_pad($value, $len, $pad_string, $pad_type);
}
if (strlen($value) > $len) {
$subject->$field = substr($value, 0, $len);
}
Here we have strlen, substr and str_pad which are non-mb safe. Which will result in various problems:
Ex. take a cyrillic script string's length - strlen("тесттест") = 16 instead of 8, so our validation will fail if we ask for strings of less than 15 characters.
An even bigger issue will ensue with the substr which may cut a unicode character in half(offtopic: slashing symbols sounds funny though) - we will get a random ascii character.
Shouldn't we instead switch to the mb functions?
$filter->sanitize('value')->to('int')->useBlankValue(0);
$subject = ['value' => 'abc'];
$filter->apply($subject);
PHP Notice: Uninitialized string offset: 0 in aura\filter\src\Rule\Sanitize\Integer.php on line 66
Instantiation in read me says
<?php
use Aura\Filter\FilterFactory;
$filter_factory = new FilterFactory();
$filter = $filter_factory->newFilter();
?>
But newFilter()
is not a method available in FilterFactory
class
Basically, almost all of the read me needs to be updated to reflect the new usage
Quoting the new function from my commit(waiting for merge)
protected function applyRule($subject)
{
$added = false;
$rule = $this->locator->get($this->rule);
$args = $this->args;
array_unshift($args, $this->field);
array_unshift($args, $subject);
if(!isset($subject->{$this->field}) && !property_exists($subject,$this->field))
{
$subject->{$this->field} = null;
$added = true;
}
$result = call_user_func_array($rule, $args);
if($added && property_exists($subject,$this->field) && $subject->{$this->field} === null) {
unset($subject->{$this->field});
}
return $result;
}
Cons: it will fail if you depend on it for add for example null to an array=>say we have $a=[];
and you expect to get $a=[b=>null]; out of it.
and the other options:
just return false in applyRule on missing property(not good imho), since the property could be allowed to be null. Hence obj={a:null} would be the same as obj={};(sorry for the json notation). So if obj->a can be null, then why can't obj->a be missing? Also if we for example want a remove rule, we should not get an error if it's already removed, right?
in all rules which don't allow null values=>do explicit isset/has_property(also those two are different) checks (too time consuming for me to do right now, but perhaps the best solution).
Maybe I've missed something, and hopefully this can be closed pretty quickly.
$f->sanitize('member-url')->toBlankOr('callback', function ($subject, $field) {
if (strlen($subject->$field) > 0 && strpos($subject->$field, 'http://') !== false) {
$subject->$field = 'http://'.$subject->$field;
}
});
$f->validate('member-url')->isBlankOr('url')->asSoftRule('Your website should be a valid url.');
Basically, if the field has a value, I want to ensure it starts with http
, but this combination always gives me:
member-url should have sanitized to callback(*Closure*)
Can anyone triage what's wrong here?
Try the following:
$filter = $filter_factory->newSubjectFilter();
$filter->validate('age')->is('callback', function($s, $f) {
return false;
});
// PHP Catchable fatal error: Object of class Closure could not be converted to string in ...
// filter/src/Spec/Spec.php on line 324
Seems that there should be a check to see if args is a Closure.
NOTE: if you use asHardRule('whatever')
it works
// From Spec.php line 324ish
protected function getDefaultMessage()
{
$message = $this->rule;
if ($this->args) {
$message .= '(' . implode(', ', $this->args) . ')';
}
return $message;
}
Even we have the field the addMessages
throws notices
email in vendor/aura/filter/src/Aura/Filter/RuleCollection.php on line 460
if (! $this->messages[$field]) {
$this->messages[$field] = [];
}
Good to check isset($this->messages[$field])
Hi,
Let's say i have following POST array from form:
POST = array (
'firstname' => 'John',
'lastname' => 'Connor',
'custom_fields' => array(
'custom_field' => 'some_value',
'another_one' => 'some_value',
);
);
Now i'm creating new aura filter:
use Aura\FilterFactory;
$filter_factory = new FilterFactory();
$filter = $filter_factory->newFilter();
And i'm applying all filter rules...
$filter->validate('firstname')->isNot('blank');
$filter->validate('firstname')->is('alpha');
$filter->validate('firstname')->is('strlenMin', 3);
$filter->sanitize('firstname')->to('string');
$filter->validate('lastname')->isNot('blank');
$filter->validate('lastname')->is('alpha');
$filter->validate('lastname')->is('strlenMin', 3);
$filter->sanitize('lastname')->to('string');
Except... yeah. How i can validate all fields inside custom_fields array?
I've tried stuff like
$filter->validate('custom_fields/custom_field')->isNot('blank');
and even
$filter->validate('custom_fields')->('custom_field')->isNot('blank');
I know i could just make another filter and apply my subarray into it, but i don't want to
In the readme.md is saying newFilter() but the release 1.0.2 that I downloaded is actualy newInstance() the name of the method.
Multidimensional Subjects, Variable Length Members, and the possible case for
injecting a factory into a subject filter?
I have a couple use cases which I have solved in various hacky ways and am
curious if others have insight into better solutions, or if a PR is in order.
I think it might be useful if the SubjectFilter
had the FilterFactory
injected into it, so that I could create new 'subfilters' (probably just another
SubjectFilter
) to validate members of the subject which are themselves data
structures (array/objects) which have their own criteria.
// Given something like this:
$data = [
'id' => 1,
'user' => [
'name' => 'Foo',
'age' => 42,
],
'url' => 'http://example.com'
];
// I want to be able to do something like this:
$subject = $factory->newSubjectFilter('My\Filter');
// .. in My\Filter::init()
$this->validate('id')->is('int');
$this->validate('url')->is('url');
$user = $this->subfilter('user'); // create a "sub-filter" ?
// $user = $this->subfilter('user', 'My\Filter\User'); // maybe pass class
$user->validate('given-name')->isNotBlank();
$user->validate('age')->is('int');
// or if not injecting the factory, and providing a method, perhaps a way to
// just inject a filter to a spec? but I don't think I like this as much.
$subject = $factory->newSubjectFilter('My\Filter');
$name = $factory->newSubjectFilter('My\Filter\Name');
$subject->setSubFilter('name', $name);
Additionally, I often have a situation where user fills out a HTML form with
some JS crap to add additional fields of some kind, ie:
<!-- //... other inputs and stuff -->
<div>
<label for="date1">Date</label>
<input id="date1" name="dates[]" />
</div>
<div>
<label for="date2">Date</label>
<input id="date2" name="dates[]" />
</div>
<button>click to add another date</button>
I think I want to be able to do something like this:
// My\Filter::init()
//.. init validation/sanitize other inputs...
// some kind of array_filter function?
// ie. unset blank values?
$this->sanitize('dates')->to('nonblank');
$this->validate('dates')->is(
'array', // subject? collection? countable?
$minCount,
$maxCount
);
// This is a little weird because I don't think this would be a value filter or
// a subjct filter?
$dateSpec = $this->newSubjectValueFilter();
// set some rules
$dateSpec->validate()->isNotBlank();
$dateSpec->validate()->is('dateTime');
$dateSpec->sanitize()->to('dateTime', 'Y-m-d');
// use spec for each
$this->filterEach('dates', $dateSpec);
One weird thing here is that $dateSpec
isn't really a SubjectFilter
as it's
not a collection of fields itself, but also not a ValueFilter
as I don't think
you can configure a spec for a value filter like you do a subject filter, it's
more a one off thing, right?
... so anyway. Any thoughts here? Am I approaching this wrong? People solve
similar problems in different ways? Or maybe have similar issues and think we
should try to hash out a solution?
Do you have any suggestions on how to remove html during sanitization?
One thing I like in v2 is usage of
$email = new Aura\Filter\Email('[email protected]');
$email->isValid();
like interface. I am not sure whether this is possible currently.
But just a wishlist . The idea is usage of filters in DDD as Mathias Verras mentioned in some slides. So we don't need to inject anything to factory or use it.
In https://github.com/auraphp/Aura.Filter/blob/master/src/Aura/Filter/Rule/Upload.php
protected function validate($file_ext = null)
We have never checked the extension.
Translator triggers an error with message "PHP Notice: Array to string conversion in vendor/aura/filter/src/Aura/Filter/Translator.php on line 72" when the "inValues" filter fails to pass.
Test case:
$values = range(0, 9);
$object = (object) [
'field' =>11
];
$filter = require 'vendor/aura/filter/scripts/instance.php';
$filter->addSoftRule('field', $filter::IS, 'inValues', $values);
$filter->values($object);
Full error:
Notice: Array to string conversion in vendor/aura/filter/src/Aura/Filter/Translator.php on line 72
Call Stack:
0.0001 227464 1. {main}() test.php:0
0.0045 1053584 2. Aura\Filter\RuleCollection->values() test.php:12
0.0046 1056192 3. Aura\Filter\RuleCollection->addMessage() vendor/aura/filter/src/Aura/Filter/RuleCollection.php:365
0.0046 1056520 4. Aura\Filter\Translator->translate() vendor/aura/filter/src/Aura/Filter/RuleCollection.php:418
0.0046 1057088 5. str_replace() vendor/aura/filter/src/Aura/Filter/Translator.php:72
Validating code ends up requiring lots of if statements. Filter should be callable without worrying about its response, with exceptions throw to halt execution, when necessary.
for example:
Boilerplate:
$this->filter(...);
$data = ['foo' => 'bar'];
Before:
$success = $filter->values($data);
if (! $success) {
$messages = $filter->getMessages();
// do something with $messages;
}
After:
// exceptions can be caught anywhere in the stack
$filter->values($data);
Its good to say the field needs a minimum of 9 characters or what ever . Currently in the error we will not be getting all the details like what is the minimum length and stuffs.
array(3) {
'userid' =>
array(1) {
[0] =>
array(6) {
'field' =>
string(6) "userid"
'method' =>
string(2) "is"
'name' =>
string(5) "email"
'params' =>
array(0) {
...
}
'message' =>
string(12) "FILTER_EMAIL"
'type' =>
string(9) "HARD_RULE"
}
}
'password' =>
array(1) {
[0] =>
array(6) {
'field' =>
string(8) "password"
'method' =>
string(2) "is"
'name' =>
string(9) "strlenMin"
'params' =>
array(1) {
...
}
'message' =>
string(17) "FILTER_STRLEN_MIN"
'type' =>
string(9) "HARD_RULE"
}
}
'confirm_password' =>
array(1) {
[0] =>
array(6) {
'field' =>
string(16) "confirm_password"
'method' =>
string(2) "is"
'name' =>
string(12) "equalToField"
'params' =>
array(1) {
...
}
'message' =>
string(21) "FILTER_EQUAL_TO_FIELD"
'type' =>
string(9) "SOFT_RULE"
}
}
}
any use of callback is leading to Aura\Filter\Exception\RuleNotMapped: callback
There is no code to handle values that are non-scalar when sanitizing or validating booleans, instead it tries to pass the value directly through assuming it is scalar and throws php warnings
PHPUnit_Framework_Error_Warning: trim() expects parameter 1 to be string, array given in aura/filter/src/Rule/AbstractBool.php on line 45
i thought we had already tagged and did this jump
https://github.com/auraphp/Aura.Filter/blob/2.0.0/src/Spec/ValidateSpec.php
The isNotBlank
rule has vanished. The docs still say to use it.
How can I test a field is not blank and apply a specific error message to it if it is?
Hello @pmjones
I was trying to make 'required-if' rule, where the field would be required only in some conditions, but because of the fact that rules are not called to blank fields there is no workaround about this issue.
Thanks for help
George
For example, 'blog-filter' as a named collection of rules that apply to blog posts
In the FilterFactory
class there is the array for all rule classes.
Among the factories there is the one for the rule now
that create an instance of the class Aura\Filter\Rule\Validate\Now
.
Problem is that class does not exist...
It exists a sanitize rule: Aura\Filter\Rule\Sanitize\Now
, but not a validate one.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.