Skip to content

Better coercion #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 22, 2016
Merged

Better coercion #336

merged 2 commits into from
Dec 22, 2016

Conversation

shmax
Copy link
Collaborator

@shmax shmax commented Nov 26, 2016

This is a second attempt at providing coercion support. My first attempt suffered from a few faults:

  1. It piggy-backed on check()--which did not take a reference as a first argument--so only objects passed in as variables were affected by coercion.
  2. Again due to the limitation on references not always being available, I had to sort of sneak coercion in here and there where I could, and it wasn't always possible for use cases like allOf

This new approach has a number of advantages and improvements over the older one:

  • added a new top-level coerce method to kick things off. It takes a reference, and everything after that point switches between coerce and check methods as needed. check is unaffected.
  • you can coerce non-objects... eg
$value = "false";
$schema = (object)[
  "type":"boolean"
];
$validator->coerce($value, $schema); // validates
echo get_type($value); // "boolean"
  • coercion now happens more deeply in the code, at the TypeConstraint level. This means that as long as the right coerce flags are passed along and references are used where appropriate internally, coercion just sort of works properly without having to wedge additional code in all over the place.

  • The CHECK_MODE_COERCE flag is no longer necessary, so you're no longer obliged to create a Factory, which means simpler client code in most cases.

Fixes #334, #329 and #322

API Note: while this does not break the check() API, you must now use coerce() to do coercion. The CHECK_MODE_COERCE flag has been removed.

Touched a lot of files, apologies, but hopefully it's not too hard to follow.

@bighappyface @pcjacobse @digitalkaoz please review

@shmax
Copy link
Collaborator Author

shmax commented Nov 30, 2016

Please review!

@digitalkaoz
Copy link
Contributor

digitalkaoz commented Dec 1, 2016 via email

@shmax
Copy link
Collaborator Author

shmax commented Dec 1, 2016

@digitalkaoz Did you? I don't see any comments, stamps, or 'approved' events. Did I miss something?

{
$this->_check($value, $schema, $path, $i, true);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should fix the indentation here. maybe we should create a PR with php-cs-fixer & style-ci

$validator->coerce($value, $schema, $path, $i);
} else {
$validator->check($value, $schema, $path, $i);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation again

$this->_check($value, $schema, $path, $i, true);
}

protected function _check(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mh maybe we can make it even more BC by just providing a CoerceConstraint (which wraps the underlying Constraint) and does all the type checking. so in my educated guess we only pass the coerce flag to the factory so it wraps the wanted constraint with the CoerceConstraint. The Facade-Pattern it is ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so all other constraints are mostly untouched...
like this:

class CoerceConstraint
{
    public function __construct(Constraint $constraint)
    {
        $this->constraint  = $constraint;
    }

    public function check(...)
    {
        $result = $this->constraint->check(...);
        
       //do the coercion here...
    }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea, but unfortunately any kind of coercion that depends on check is a non-starter because I would have to change the first argument to a reference, which would break bc.

@digitalkaoz
Copy link
Contributor

ah now, forgot to submit the review

@shmax
Copy link
Collaborator Author

shmax commented Dec 1, 2016

Fixed the whitespace issues, thanks; one of these days I should quit being lazy and modify my IDE settings.

@shmax
Copy link
Collaborator Author

shmax commented Dec 7, 2016

@bighappyface How are your plans for the next release coming along?

@bighappyface
Copy link
Collaborator

4.1.0 with this PR?

@shmax
Copy link
Collaborator Author

shmax commented Dec 7, 2016

Sounds good. Are we waiting on anything? I believe I'll need to rebase once you merge #331

Copy link
Collaborator

@bighappyface bighappyface left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shmax please rebase

@shmax
Copy link
Collaborator Author

shmax commented Dec 11, 2016

@bighappyface Rebased.

@SereneAnt I de-simplified your code a bit; I've brought in a TypeConstraint validator to replace the stuff you were doing inline because I need coercion to work. I don't think it should slow things down much, if at all, because it pretty much does the same things you were doing, only within its own domain (so important functionality like coercion happens). If it seems like a problem to you we can just add a check for $coerce to the conditions we consider before branching into your optimized code.

Please review!

@@ -125,10 +124,14 @@ protected function incrementPath(JsonPointer $path = null, $i)
* @param JsonPointer|null $path
* @param mixed $i
*/
protected function checkArray($value, $schema = null, JsonPointer $path = null, $i = null)
protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing PHPDoc for parameter$coerse.
IMHO, $coerse flag would look better as the Factory field rather than additional method parameter.

Copy link
Collaborator Author

@shmax shmax Dec 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good eye, thank you. I've added phpdocs, following the patterns and style already in place.

As for adding $coerce as a Factory flag, well, that was the approach I tried previously. It made sense in my previous approach because I was driving everything through check, and it just became a mode flag (stored in Factory, as you suggest). The problem there was that I had no reference to work with in check, so I couldn't properly coerce in all situations.

Now that I'm driving it through an explicit function call, coerce, it's no longer really a configuration value. If one were to, say, call check immediately after calling coerce, I would just have to set any stored fields back to false, so it would be another moving part with no real value.

I was actually interested in your reaction to what I did in CollectionConstraint. Any thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look reasonable. Type check is more solid with this approach.
The performance penalty is not too big, avg time for validating 100K string array:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! If you're satisfied, please approve.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no write permissions, can't approve.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A LGTM will help

@shmax
Copy link
Collaborator Author

shmax commented Dec 14, 2016

@bighappyface please merge, or let me know what I have to do to move forward.

@SereneAnt
Copy link
Contributor

LGTM

@pcjacobse
Copy link

Sorry for the late reply, but works like a charm ;)

@shmax
Copy link
Collaborator Author

shmax commented Dec 22, 2016

@bighappyface What's the plan?

@bighappyface bighappyface merged commit d39c56a into jsonrainbow:master Dec 22, 2016
@shmax shmax deleted the coerce-v2 branch December 22, 2016 16:53
@shmax
Copy link
Collaborator Author

shmax commented Dec 29, 2016

Thank you!

@alcohol
Copy link
Contributor

alcohol commented Feb 16, 2017

Nobody noticed the mixed spaces and tabs indentations?

@shmax
Copy link
Collaborator Author

shmax commented Feb 16, 2017

Nope, nobody did, and you're commenting on a PR that was merged almost two months ago. If you can find formatting issues in the current state of the code I'll be happy to clean 'em up.

@alcohol
Copy link
Contributor

alcohol commented Feb 17, 2017

Yeah, sorry, I was going through the changes between 4.x and 5.x as I wanted to see if Composer can easily upgrade/be compatible with the latest major version (we currently are compatible with 3.x and 4.x). Noticed some inconsistencies in the change sets. Hence my code-style PR submission :-)

@bighappyface
Copy link
Collaborator

Thanks for the help ensuring things stay smooth with Composer, @alcohol.

Being a dependency for Composer is our crown jewel 👑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants