Skip to content

Commit

Permalink
Support arbitrary "provides" and "depends" validator options with acc…
Browse files Browse the repository at this point in the history
…ess to argument base parts, closes agavi#1199
  • Loading branch information
dzuelke committed Dec 4, 2011
1 parent 6ecffb4 commit d158fec
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ AGAVI CHANGELOG
1.1.0 beta1 (June ??, 2009)
---------------------------

ADD: Support arbitrary "provides" and "depends" validator options with access to argument base parts (#1199) (David)
ADD: Allow calls to PHP functions in configuration file transformations (#1456) (David)
ADD: PSR-0 compatible namespaces autoloader (#1454) (David)
ADD: Allow validators to specify result code for exported data (#1453) (David)
Expand Down
6 changes: 6 additions & 0 deletions RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ Validators
It is now possible to export request data arguments to a different source. This is done by either passing an AgaviValidationArgument object as the second argument to AgaviValidator::export(), or, if the validator does not provide an argument name or only a name, but not an object, via the "export_to_source" parameter of the validator.
Furthermore, a new third parameter in AgaviValidator::export() now accepts the result code to use when exporting; it defaults to AgaviValidator::SUCCESS and may be used to for instance require validation of the exported value by using AgaviValidator::NOT_PROCESSED.

The "provides" and "depends" options of a validator now take an sprintf() string where you can use position specifiers to access parts of the argument base.
This means that having a "provides" value of "foobar" with an argument base of "foo[]" will now provide into "foobar" for every iteration, and not into "foo[$key][foobar]" like in 1.0. To get this old behavior back in this example, you'd have to use "foo[%2$s][foobar]" or "%1$s[%2$s][foobar]" as the value for "provides". The referencing of argument base parts works exactly like in "export" strings.
For "depends", the exact same syntax and behavior is now used; the old behavior was for a plain "foobar" to depend on just "foobar" no matter the argument base, and for "[foobar]" with an argument base of "foo[]" to depend on "foo[$key][foobar]", which was a bit confusing and also meant that you could only ever depend on provides from the same argument base. To depend on the "provides" results for the same keys as in the example above, you would again use "foo[%2$s][foobar]" or "%1$s[%2$s][foobar]" (but if we assume that this second validator had "lulz[]" as the argument base, then only "foo[%2$s][foobar]" would work of course).
If your validation config is from the 1.0 namespace, then the old behavior is retained automatically, so if you want to take advantage of this new functionality, you must bump the config namespace to 1.1.
Some more examples can be found in ticket #1199 (and also in #1073 where the same syntax is discussed for the exporting feature).

Filters
-------
AgaviIFilter::executeOnce() has been deprecated; in most cases, you can simply use execute() instead, see UPGRADING for further information.
Expand Down
20 changes: 20 additions & 0 deletions src/config/xsl/validators.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="http://php.net/xsl"
xmlns:envelope_0_11="http://agavi.org/agavi/1.0/config"
xmlns:validators_1_0="http://agavi.org/agavi/config/parts/validators/1.0"
xmlns:validators_1_1="http://agavi.org/agavi/config/parts/validators/1.1"
Expand Down Expand Up @@ -31,6 +32,25 @@
</xsl:template>

<!-- 1.0 backwards compatibility for 1.1 -->
<!-- <validator> elements with <arguments base="..." />... -->
<xsl:template match="validators_1_0:validator[validators_1_0:arguments[@base]]">
<xsl:element name="{local-name()}" namespace="{$validators_1_1}">
<!-- ... have their attributes copied, but not "provides", or "depends" with a value starting on "["... -->
<xsl:copy-of select="@*[not(local-name()='provides' and not(local-name()='depends' and substring(.,1,1)='['))]" />
<!-- since those get special rules -->
<xsl:apply-templates select="@provides | @depends[substring(.,1,1)='[']" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<!-- rule for "provides" attribute in <validator> with <arguments base="..." /> -->
<xsl:template match="validators_1_0:validator[validators_1_0:arguments[@base]]/@provides">
<xsl:attribute name="provides"><xsl:value-of select="php:function('AgaviDependencyManager::populateArgumentBaseKeyRefs', concat(../validators_1_0:arguments/@base,'[',.,']'))" /></xsl:attribute>
</xsl:template>
<!-- rule for "depends" attribute with value starting on "[" in <validator> with <arguments base="..." /> -->
<xsl:template match="validators_1_0:validator[validators_1_0:arguments[@base]]/@depends[substring(.,1,1)='[']">
<xsl:attribute name="depends"><xsl:value-of select="php:function('AgaviDependencyManager::populateArgumentBaseKeyRefs', concat(../validators_1_0:arguments/@base,.))" /></xsl:attribute>
</xsl:template>
<!-- rule for all other elements -->
<xsl:template match="validators_1_0:*">
<xsl:element name="{local-name()}" namespace="{$validators_1_1}">
<xsl:copy-of select="@*" />
Expand Down
50 changes: 42 additions & 8 deletions src/validator/AgaviDependencyManager.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ public function clear()
*/
public function checkDependencies(array $tokens, AgaviVirtualArrayPath $base)
{
$root = new AgaviVirtualArrayPath('');
$currentParts = $base->getParts();
foreach($tokens as $token) {
$path = $root;
if(substr($token, 0, 1) == '[') {
// the dependency we need to check is relative
$path = $base;
if($currentParts && strpos($token, '%') !== false) {
// the depends attribute contains sprintf syntax
$token = vsprintf($token, $currentParts);
}

if(!$path->getValueByChildPath($token, $this->depData)) {

$path = new AgaviVirtualArrayPath($token);
if(!$path->getValue($this->depData)) {
return false;
}
}
Expand All @@ -87,9 +87,43 @@ public function checkDependencies(array $tokens, AgaviVirtualArrayPath $base)
*/
public function addDependTokens(array $tokens, AgaviVirtualArrayPath $base)
{
$currentParts = $base->getParts();
foreach($tokens as $token) {
$base->setValueByChildPath($token, $this->depData, true);
if($currentParts && strpos($token, '%') !== false) {
// the depends attribute contains sprintf syntax
$token = vsprintf($token, $currentParts);
}

$path = new AgaviVirtualArrayPath($token);
$path->setValue($this->depData, true);
}
}

/**
* Populate key references in an argument base string if necessary.
* Fills only empty bracket positions with an sprintf() offset placeholder.
* Example: foo[][bar][] as input will return foo[%2$s][bar][%4$s] as output.
* This is used in validate.xsl to convert pre-1.1 provides/depends behavior.
*
* @param string The argument base string.
*
* @return string The argument base string with empty brackets filled with
* correct sprintf() position specifiers.
*
* @author David Zülke <[email protected]>
* @since 1.1.0
*/
public static function populateArgumentBaseKeyRefs($string)
{
$index = 1;
return preg_replace_callback(
'#\[([^\]]*)\]#',
function($matches) use(&$index) {
$index++; // always increment so static key parts are "skipped" properly
return $matches[1] !== '' ? $matches[0] : '[%'.$index.'$s]'; // leave parts other than "[]" intact, else inject numeric accessor
},
$string
);
}
}
?>

0 comments on commit d158fec

Please sign in to comment.