Example #1
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input === null) {
         goto done;
     }
     cast:
     if (is_string($input) && $this->allowString) {
         $cast = trim($input);
         if ($cast === "") {
             $cast = null;
         } elseif (is_numeric($input)) {
             if (ctype_digit($input)) {
                 $cast = (int) ($input + 0);
             } else {
                 $cast = (double) ($input + 0.0);
             }
         } else {
             goto cast_done;
         }
         $ctx->setChange(Change::Internal);
         $input = $cast;
         cast_done:
     }
     if ($input === null) {
         goto done;
     }
     type:
     if (!is_float($input) && (!$this->allowInt || !is_int($input))) {
         $ctx->addReason($this, ['id' => 'double.invalidType']);
         goto done;
     }
     minmax:
     if ($this->min !== null && $this->max !== null) {
         if ($input < $this->min || $input > $this->max) {
             $ctx->addReason($this, ['id' => 'double.between', 'params' => ['atLeast' => $this->min, 'atMost' => $this->max]]);
         }
     } elseif ($this->min !== null) {
         if ($input < $this->min) {
             $ctx->addReason($this, ['id' => 'double.atLeast', 'params' => ['atLeast' => $this->min]]);
         }
     } elseif ($this->max !== null) {
         if ($input > $this->max) {
             $ctx->addReason($this, ['id' => 'double.atMost', 'params' => ['atMost' => $this->max]]);
         }
     }
     divisible_by:
     if ($this->divisibleBy !== null) {
         // eeeeewwwww!
         if (fmod($input, $this->divisibleBy) > self::TOLERANCE) {
             $ctx->addReason($this, ['id' => 'double.divisibleBy', 'params' => ['divisibleBy' => $this->divisibleBy]]);
         }
     }
     done:
     return $input;
 }
Example #2
0
 protected function applyValue($input, Context $ctx)
 {
     $output = $input;
     if ($input === null || $input === true || $input === false) {
         goto done;
     }
     if ($this->allowLoose) {
         $checkValue = $input;
         if (is_string($checkValue)) {
             $checkValue = mb_strtolower(\Normalizer::normalize($checkValue));
         }
         if (in_array($checkValue, $this->trueValues ?: self::$defaultLooseTrue, true)) {
             $output = true;
         } elseif (in_array($checkValue, $this->falseValues ?: self::$defaultLooseFalse, true)) {
             $output = false;
         }
     }
     if ($output !== false && $output !== true) {
         $ctx->addReason($this, ['id' => 'bool.invalid']);
     }
     done:
     if ($output !== $input) {
         $ctx->setChange(Change::Internal);
     }
     return $output;
 }
Example #3
0
 function apply($input, \Fulfil\Context $ctx)
 {
     if ($input !== null) {
         $ctx->addReason($this, ['id' => 'absent.invalid']);
     }
     done:
     return $input;
 }
Example #4
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input === null) {
         goto done;
     }
     cast:
     if (is_string($input) && $this->allowString) {
         $cast = trim($input);
         $sign = isset($cast[0]) && $cast[0] == '-';
         $cast = ltrim($cast, '- ');
         if ($cast === "") {
             $cast = null;
         } elseif (ctype_digit($cast)) {
             $cast = (int) ltrim($cast, "0");
         } elseif (preg_match('~^ 0x[\\da-fA-F]+ $~ ux', $cast)) {
             $cast = hexdec($cast);
         } else {
             goto cast_done;
         }
         if ($sign) {
             $cast = -$cast;
         }
         $ctx->setChange(Change::Internal);
         $input = $cast;
         cast_done:
     }
     if ($input === null) {
         goto done;
     }
     type:
     if (!is_int($input)) {
         $ctx->addReason($this, ['id' => 'int.invalidType']);
         goto done;
     }
     minmax:
     if ($this->min !== null && $this->max !== null) {
         if ($input < $this->min || $input > $this->max) {
             $ctx->addReason($this, ['id' => 'int.between', 'params' => ['atLeast' => $this->min, 'atMost' => $this->max]]);
         }
     } elseif ($this->min !== null) {
         if ($input < $this->min) {
             $ctx->addReason($this, ['id' => 'int.atLeast', 'params' => ['atLeast' => $this->min]]);
         }
     } elseif ($this->max !== null) {
         if ($input > $this->max) {
             $ctx->addReason($this, ['id' => 'int.atMost', 'params' => ['atMost' => $this->max]]);
         }
     }
     divisibleBy:
     if ($this->divisibleBy !== null) {
         if ($input % $this->divisibleBy !== 0) {
             $ctx->addReason($this, ['id' => 'int.divisibleBy', 'params' => ['divisibleBy' => $this->divisibleBy]]);
         }
     }
     done:
     return $input;
 }
Example #5
0
 function apply($input, \Fulfil\Context $ctx)
 {
     if (!$input instanceof $this->class) {
         $found = is_object($input) ? get_class($input) : gettype($input);
         $ctx->addReason($this, ['id' => 'class.invalidType', 'params' => ['found' => $found]]);
     } else {
         $input = $this->getSchema()->apply($input, $ctx);
     }
     return $input;
 }
Example #6
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input === null) {
         goto done;
     }
     if (!is_array($input) && !$input instanceof \Traversable) {
         $ctx->addReason($this, ['id' => 'iterable.invalidType']);
     }
     done:
     return $input;
 }
Example #7
0
 protected function applyValue($input, \Fulfil\Context $ctx)
 {
     if ($input === null) {
         goto done;
     }
     if (!$input instanceof $this->class) {
         $found = Func::getType($input);
         $ctx->addReason($this, ['id' => 'instance.invalidType', 'params' => ['expected' => $this->class, 'found' => $found]]);
     } elseif ($input instanceof \Fulfil\Lang\SelfChecker) {
         $input->check($ctx);
     }
     done:
     return $input;
 }
Example #8
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input !== null && !$input instanceof \DateTime) {
         $ctx->addReason($this, ['id' => 'date.invalidType']);
         goto done;
     }
     length:
     if ($this->min !== null && $this->max !== null) {
         if ($input < $this->min || $input > $this->max) {
             $ctx->addReason($this, ['id' => 'date.between', 'params' => ['input' => $input, 'min' => $this->min, 'max' => $this->max]]);
         }
     } elseif ($this->min !== null) {
         if ($input < $this->min) {
             $ctx->addReason($this, ['id' => 'date.atLeast', 'params' => ['input' => $input, 'min' => $this->min]]);
         }
     } elseif ($this->max !== null) {
         if ($input > $this->max) {
             $ctx->addReason($this, ['id' => 'date.atMost', 'params' => ['input' => $input, 'max' => $this->max]]);
         }
     }
     done:
     return $input;
 }
Example #9
0
 function validate(array $properties, Context $ctx)
 {
     $dateMin = $properties['dateMin'];
     $dateMax = $properties['dateMax'];
     if (!$dateMin || !$dateMax) {
         return;
     }
     if (!$ctx->pathValid($this->dateMin) || !$ctx->pathValid($this->dateMax)) {
         return;
     }
     if (!$dateMin instanceof \DateTime || !$dateMax instanceof \DateTime) {
         return;
     }
     if ($dateMin > $dateMax) {
         $ctx->addReason($this, ['id' => 'dateRange.invalid', 'params' => ['min' => $dateMin, 'max' => $dateMax]]);
     }
 }
Example #10
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input === null) {
         goto done;
     }
     if (!is_string($input)) {
         $ctx->addReason($this, ['id' => 'string.invalidType']);
         goto done;
     }
     empty_mode:
     if ($input === '') {
         $emptyMode = $this->emptyMode ?: ($this->required ? self::EMPTY_BLOCK : self::EMPTY_ALLOW);
         if ($emptyMode == self::EMPTY_BLOCK) {
             $ctx->addReason($this, ['id' => 'string.empty']);
             goto done;
         } elseif ($emptyMode == self::EMPTY_SET_NULL) {
             $input = null;
             $ctx->setChange(Change::Internal);
             goto done;
         }
     }
     bytes:
     if ($this->bytesMax !== null && ($bytes = strlen($input)) > $this->bytesMax) {
         $ctx->addReason($this, ['id' => 'string.bytesAtMost', 'params' => ['bytes' => $bytes, 'atMost' => $this->bytesMax]]);
     }
     length:
     $len = $this->length !== null || $this->lengthMax !== null || $this->lengthMin !== null ? mb_strlen($input, "UTF-8") : null;
     if ($this->length !== null) {
         if ($len != $this->length) {
             $ctx->addReason($this, ['id' => 'string.length', 'params' => ['length' => $len, 'expected' => $this->length]]);
         }
     } elseif ($this->lengthMin !== null && $this->lengthMax !== null) {
         if ($len < $this->lengthMin || $len > $this->lengthMax) {
             $ctx->addReason($this, ['id' => 'string.lengthBetween', 'params' => ['length' => $len, 'atLeast' => $this->lengthMin, 'atMost' => $this->lengthMax]]);
         }
     } elseif ($this->lengthMin !== null) {
         if ($len < $this->lengthMin) {
             $ctx->addReason($this, ['id' => 'string.lengthAtLeast', 'params' => ['length' => $len, 'atLeast' => $this->lengthMin]]);
         }
     } elseif ($this->lengthMax !== null) {
         if ($len > $this->lengthMax) {
             $ctx->addReason($this, ['id' => 'string.lengthAtMost', 'params' => ['length' => $len, 'atMost' => $this->lengthMax]]);
         }
     }
     pattern:
     if ($input !== '') {
         $this->applyPattern($input, $ctx);
     }
     done:
     return $input;
 }
Example #11
0
 protected function applyValue($input, Context $ctx)
 {
     $valid = false;
     foreach ($this->checks as $id => $check) {
         $newCtx = new Context();
         $input = $check->apply($input, $newCtx);
         $flat = $newCtx->flatten();
         if ($flat->valid) {
             $valid = $flat->valid;
             if ($change = $flat->change) {
                 $ctx->setChange($change);
             }
             if (!$flat->complete) {
                 $ctx->setIncomplete($this);
             }
             break;
         }
     }
     if (!$valid) {
         $ctx->addReason($this, ['id' => 'any.invalid']);
     }
     return $input;
 }
Example #12
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input === null) {
         goto done;
     }
     $isArray = is_array($input);
     $len = null;
     type:
     if (!$isArray && !$input instanceof \Traversable && !$input instanceof \Countable) {
         $ctx->addReason($this, ['id' => 'array.invalidType', 'params' => ['type' => Func::getType($input)]]);
         goto done;
     }
     $len = $this->length !== null || $this->lengthMax !== null || $this->lengthMin !== null || $this->unique !== null ? count($input) : null;
     length:
     if ($this->length) {
         if ($len != $this->length) {
             $ctx->addReason($this, ['id' => 'array.length', 'params' => ['len' => $len, 'expected' => $this->length]]);
         }
     } elseif ($this->lengthMin !== null && $this->lengthMax !== null) {
         if ($len < $this->lengthMin || $len > $this->lengthMax) {
             $ctx->addReason($this, ['id' => 'array.lengthBetween', 'params' => ['len' => $len, 'min' => $this->lengthMin, 'max' => $this->lengthMax]]);
         }
     } elseif ($this->lengthMin !== null) {
         if ($len < $this->lengthMin) {
             $ctx->addReason($this, ['id' => 'array.lengthAtLeast', 'params' => ['len' => $len, 'min' => $this->lengthMin]]);
         }
     } elseif ($this->lengthMax !== null) {
         if ($len > $this->lengthMax) {
             $ctx->addReason($this, ['id' => 'array.lengthAtMost', 'params' => ['len' => $len, 'max' => $this->lengthMax]]);
         }
     }
     unique:
     if ($this->unique !== null && $this->unique) {
         if ($len === null) {
             $len = count($input);
         }
         if (count(array_unique($input, SORT_REGULAR)) != $len) {
             $ctx->addReason($this, ['id' => 'array.unique']);
         }
     }
     done:
     return $input;
 }
Example #13
0
 protected function applyValue($input, Context $ctx)
 {
     if ($input === null) {
         return $input;
     }
     $properties = null;
     $isMapped = false;
     $modifiedProps = [];
     $unknown = [];
     list_properties:
     if (is_object($input)) {
         if ($this->mapper) {
             $properties = (array) $this->mapper->mapObjectToProperties($input);
             $isMapped = $properties !== null;
         }
         if ($properties === null) {
             $properties = [];
             foreach ($input as $k => &$v) {
                 $properties[$k] =& $v;
             }
         }
     } elseif (is_array($input)) {
         $properties =& $input;
     }
     input_checks:
     if (!is_array($properties)) {
         $ctx->addReason($this, ['id' => 'schema.invalidType']);
         goto done;
     }
     prop_checks:
     $checkedProps = [];
     foreach ($this->props as $propId => $checks) {
         $name = null;
         $checkedProps[$propId] = true;
         if ($checks === true) {
             goto next_prop;
         }
         if ($checks instanceof \Fulfil\CheckInterface) {
             $checks = [$checks];
         }
         foreach ($checks as $check) {
             if ($check->name()) {
                 $name = $check->name();
                 break;
             }
         }
         $ctx->push($propId, $name);
         try {
             $value = array_key_exists($propId, $properties) ? $properties[$propId] : null;
             $modified = false;
             foreach ($checks as $check) {
                 $value = $check->apply($value, $ctx);
                 $modified |= $ctx->getChange() == true;
             }
             if ($modified) {
                 $properties[$propId] = $value;
                 $modifiedProps[$propId] = $value;
             }
         } finally {
             $ctx->pop($propId);
         }
         next_prop:
     }
     $unknown = array_keys(array_diff_key($properties, $checkedProps));
     foreach ($unknown as $up) {
         foreach ($this->ignore as $pattern) {
             if (Func::userPatternMatch($pattern, $up)) {
                 goto next_unknown;
             }
         }
         if ($this->filterUnknownProps) {
             // important: without this, if you attempt to repopulate an object with
             // the result later, you can run into errors.
             unset($properties[$up]);
             $ctx->setChange(Change::Internal);
         }
         $ctx->addReason($this, ['id' => 'schema.propUnknown', 'params' => ['prop' => $up]]);
         next_unknown:
     }
     rules:
     foreach ($this->rules as $rule) {
         $ruleProps = [];
         foreach ($rule->listProps() as $k => $p) {
             $ruleProps[$k] = isset($properties[$p]) ? $properties[$p] : null;
         }
         $rule->validate($ruleProps, $ctx);
     }
     done:
     if ($modifiedProps && $isMapped) {
         $this->mapper->populateObject($input, (object) $modifiedProps);
     }
     return $input;
 }
Example #14
0
 protected function applyValue($input, Context $ctx)
 {
     $output = $input;
     cast:
     $allowString = $this->allowString === null ? true : $this->allowString;
     try {
         $cast = $input;
         if ($allowString && is_string($input)) {
             if ($input === '') {
                 $cast = null;
             } else {
                 $cast = BigNumbers\Decimal::fromString($input);
             }
         } elseif ($this->allowInt && is_int($input)) {
             $cast = BigNumbers\Decimal::fromInteger($input);
         } elseif ($this->allowDouble && is_float($input)) {
             $cast = BigNumbers\Decimal::fromFloat($input);
         }
         if ($cast !== $input) {
             $ctx->setChange(Change::Internal);
         }
         $output = $cast;
     } catch (\Exception $ex) {
         $ctx->addReason($this, ['id' => 'decimal.invalid']);
         goto done;
     }
     // must cast before this so we can cast to null
     if ($output === null) {
         goto done;
     }
     type:
     if (!$output instanceof BigNumbers\Decimal) {
         $ctx->addReason($this, ['id' => 'decimal.invalid']);
         goto done;
     }
     scale:
     if ($this->scale !== null) {
         if (preg_match('/\\.([0-9]+)$/', $output . '', $match)) {
             $inScale = strlen($match[1]);
             if ($inScale > $this->scale) {
                 $ctx->addReason($this, ['id' => 'decimal.scale', 'params' => ['scale' => $inScale, 'expected' => $this->scale]]);
             }
         }
     }
     precision:
     if ($this->precision !== null) {
         $digits = preg_replace("/[^0-9]/", '', $output . '');
         $inPrecision = strlen($digits);
         if ($inPrecision > $this->precision) {
             $ctx->addReason($this, ['id' => 'decimal.precision', 'params' => ['precision' => $inPrecision, 'expected' => $this->precision]]);
         }
     }
     minmax:
     $min = $this->min !== null ? BigNumbers\Decimal::create($this->min) : null;
     $max = $this->max !== null ? BigNumbers\Decimal::create($this->max) : null;
     if ($min !== null && $max !== null) {
         if ($output->comp($min) < 0 || $output->comp($max) > 0) {
             $ctx->addReason($this, ['id' => 'decimal.between', 'params' => ['atLeast' => $min . '', 'atMost' => $max . '']]);
         }
     } elseif ($min !== null) {
         if ($output->comp($min) < 0) {
             $ctx->addReason($this, ['id' => 'decimal.atLeast', 'params' => ['atLeast' => $min . '']]);
         }
     } elseif ($max !== null) {
         if ($output->comp($max) > 0) {
             $ctx->addReason($this, ['id' => 'decimal.atMost', 'params' => ['atMost' => $max . '']]);
         }
     }
     divisibleBy:
     if ($this->divisibleBy !== null) {
         $divisibleBy = !$this->divisibleBy instanceof BigNumbers\Decimal ? BigNumbers\Decimal::create($this->divisibleBy) : $this->divisibleBy;
         if (!$output->mod($divisibleBy)->isZero()) {
             $dvFmt = $this->removeTrailingZeroes($divisibleBy);
             $ctx->addReason($this, ['id' => 'decimal.divisibleBy', 'params' => ['divisibleBy' => $dvFmt]]);
         }
     }
     done:
     if ($this->emitString && $output instanceof BigNumbers\Decimal) {
         $output = $output->innerValue();
         if (!is_string($input)) {
             $ctx->setChange(Change::Internal);
         }
     }
     return $output;
 }
Example #15
0
 function testEnsureValid()
 {
     $ctx = new Context();
     $ctx->addReason(null, ['id' => 'nope']);
     $this->setExpectedException(\Fulfil\ValidationException::class, "Failed validation:\n - nope");
     $ctx->ensureValid();
 }
Example #16
0
 private function compareString(Context $ctx, $left, $right)
 {
     if (is_string($left) && is_string($right)) {
         if (static::$intlAvailable) {
             if ($this->test(\Normalizer::normalize($left) != \Normalizer::normalize($right))) {
                 $ctx->addReason($this, ['id' => 'equal.stringNotEqual', 'params' => $properties]);
             }
         } else {
             $ctx->setIncomplete($this);
         }
     } else {
         $ctx->addReason($this, ['id' => 'equal.incomparable', 'params' => ['leftProp' => $this->left, 'rightProp' => $this->right]]);
     }
     return;
 }