protected function applyValue($input, Context $ctx) { $output = $input; if (!is_string($input)) { goto done; } $change = Change::User; nl2spc: if ($this->nl2spc) { $output = $this->nl2spcUnicode($input); $change = null; } trim: if ($this->trim) { $output = $this->trimUnicode($input); } casing: if ($this->casing) { if ($this->casing == 'lower') { $output = mb_strtolower($input); } elseif ($this->casing == 'upper') { $output = mb_strtoupper($input); } elseif ($this->casing == 'title') { $output = mb_convert_case($input, MB_CASE_TITLE); } } done: if ($output !== $input && $change !== null) { $ctx->setChange($change); } return $output; }
function testIncompleteApply() { $check = new Incomplete\Check(['incompleteExportKey' => 'check', 'incompleteType' => 'foo']); $check->apply('foo', $ctx = new Context(['allowIncomplete' => true])); $this->assertTrue($ctx->allValid()); $this->assertFalse($ctx->flatten()->complete); }
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; }
function apply($input, \Fulfil\Context $ctx) { if ($input !== null) { $ctx->addReason($this, ['id' => 'absent.invalid']); } done: return $input; }
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; }
function map($v) { foreach ($this->checks as $check) { $check->apply($v, $ctx = new \Fulfil\Context()); if ($ctx->allValid()) { return $check->map($v); } } return $v; }
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; }
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; }
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; }
function assertFormValid($in, $check, $value, $config = []) { $fs = $this->createFormSchema($check, $config); $out = $fs->applyValue($this->form($in), $ctx = new Context()); if (is_object($value)) { $this->assertEquals($value, $out['foo']); } else { $this->assertSame($value, $out['foo']); } $this->assertTrue($ctx->flatten()->valid); }
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; }
function testDefault() { $list = new Check\List_(['items' => [new Check\Int_()], 'defaultItem' => new Check\String_()]); $list->apply([1], $ctx = new Context()); $this->assertTrue($ctx->allValid()); $list->apply([1, '1', '1'], $ctx = new Context()); $this->assertTrue($ctx->allValid()); $list->apply([null, null], $ctx = new Context()); $this->assertTrue($ctx->allValid()); $list->apply(['1'], $ctx = new Context()); $flat = $ctx->flatten(); $this->assertFalse($flat->valid); $this->assertCount(1, $flat->messages); $this->assertEquals(['int', 'invalidType'], $flat->messages[0]->id); }
/** * @param path mixed Array or string path to context node containing * messages, i.e. ['foo', 'bar'] or 'foo.bar' * @param depth int Internal */ public function formatContext(\Fulfil\Context $context, $path = [], $depth = null) { $formatted = []; if (is_string($path)) { $path = explode('.', $path); } if ($depth === null) { $depth = count($path); } $node = !$path ? $context->root() : $context->root()->child($path); if (!$node) { throw new \InvalidArgumentException("Node could not be found at path " . Message::implodePath($path)); } return $this->formatNode($node, $depth); }
protected function applyValue($input, Context $ctx) { if ($input === null) { goto done; } if (!is_string($input) && is_scalar($input)) { $input = (string) $input; $ctx->setChange(Change::Internal); } elseif (is_object($input) && method_exists($input, '__toString')) { $input = $input->__toString(); $ctx->setChange(Change::Internal); } done: return $input; }
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]]); } }
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; }
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; }
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; }
protected function applyValue($input, Context $ctx) { if (!is_string($input)) { return $input; } if ($input === '') { $ctx->setChange(Change::Internal); return null; } $found = false; foreach ($this->formats as $format) { if ($this->tz) { $dt = \DateTime::createFromFormat($format, $input, $this->tz); } else { $dt = \DateTime::createFromFormat($format, $input); } if ($dt !== false) { $ctx->setChange(Change::User); $input = $dt; break; } } return $input; }
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; }
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; }
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; }
function apply($input, Context $ctx) { $ctx->setIncomplete($this); return $input; }
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; }
function validate(array $input, Context $ctx) { $ctx->setIncomplete($this); }
function testEnsureValid() { $ctx = new Context(); $ctx->addReason(null, ['id' => 'nope']); $this->setExpectedException(\Fulfil\ValidationException::class, "Failed validation:\n - nope"); $ctx->ensureValid(); }