# packeta/1.6.1/deps/nette/schema/src/Schema/Elements/Structure.php

Packeta, version 1.6.1. 158 lines.

- Page: https://pluginprobe.com/plugins/packeta/1.6.1/code/deps/nette/schema/src/Schema/Elements/Structure.php
- Raw: https://pluginprobe.com/plugins/packeta/1.6.1/raw/deps/nette/schema/src/Schema/Elements/Structure.php
- Modified: 2023-08-30T08:36:18+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/packeta/1.6.1/code/deps/nette/schema/src/Schema/Elements/Structure.php#L10-L20`.

```php
<?php

/**
 * This file is part of the Nette Framework (https://nette.org)
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
 */
declare (strict_types=1);
namespace Packetery\Nette\Schema\Elements;

use Packetery\Nette;
use Packetery\Nette\Schema\Context;
use Packetery\Nette\Schema\Helpers;
use Packetery\Nette\Schema\Schema;
final class Structure implements Schema
{
    use Base;
    use \Packetery\Nette\SmartObject;
    /** @var Schema[] */
    private $items;
    /** @var Schema|null  for array|list */
    private $otherItems;
    /** @var array{?int, ?int} */
    private $range = [null, null];
    /** @var bool */
    private $skipDefaults = \false;
    /**
     * @param  Schema[]  $items
     */
    public function __construct(array $items)
    {
        (function (Schema ...$items) {
        })(...\array_values($items));
        $this->items = $items;
        $this->castTo = 'object';
        $this->required = \true;
    }
    public function default($value) : self
    {
        throw new \Packetery\Nette\InvalidStateException('Structure cannot have default value.');
    }
    public function min(?int $min) : self
    {
        $this->range[0] = $min;
        return $this;
    }
    public function max(?int $max) : self
    {
        $this->range[1] = $max;
        return $this;
    }
    /**
     * @param  string|Schema  $type
     */
    public function otherItems($type = 'mixed') : self
    {
        $this->otherItems = $type instanceof Schema ? $type : new Type($type);
        return $this;
    }
    public function skipDefaults(bool $state = \true) : self
    {
        $this->skipDefaults = $state;
        return $this;
    }
    /********************* processing ****************d*g**/
    public function normalize($value, Context $context)
    {
        if ($prevent = \is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
            unset($value[Helpers::PREVENT_MERGING]);
        }
        $value = $this->doNormalize($value, $context);
        if (\is_object($value)) {
            $value = (array) $value;
        }
        if (\is_array($value)) {
            foreach ($value as $key => $val) {
                $itemSchema = $this->items[$key] ?? $this->otherItems;
                if ($itemSchema) {
                    $context->path[] = $key;
                    $value[$key] = $itemSchema->normalize($val, $context);
                    \array_pop($context->path);
                }
            }
            if ($prevent) {
                $value[Helpers::PREVENT_MERGING] = \true;
            }
        }
        return $value;
    }
    public function merge($value, $base)
    {
        if (\is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
            unset($value[Helpers::PREVENT_MERGING]);
            $base = null;
        }
        if (\is_array($value) && \is_array($base)) {
            $index = 0;
            foreach ($value as $key => $val) {
                if ($key === $index) {
                    $base[] = $val;
                    $index++;
                } elseif (\array_key_exists($key, $base)) {
                    $itemSchema = $this->items[$key] ?? $this->otherItems;
                    $base[$key] = $itemSchema ? $itemSchema->merge($val, $base[$key]) : Helpers::merge($val, $base[$key]);
                } else {
                    $base[$key] = $val;
                }
            }
            return $base;
        }
        return Helpers::merge($value, $base);
    }
    public function complete($value, Context $context)
    {
        if ($value === null) {
            $value = [];
            // is unable to distinguish null from array in NEON
        }
        $this->doDeprecation($context);
        if (!$this->doValidate($value, 'array', $context) || !$this->doValidateRange($value, $this->range, $context)) {
            return;
        }
        $errCount = \count($context->errors);
        $items = $this->items;
        if ($extraKeys = \array_keys(\array_diff_key($value, $items))) {
            if ($this->otherItems) {
                $items += \array_fill_keys($extraKeys, $this->otherItems);
            } else {
                $keys = \array_map('strval', \array_keys($items));
                foreach ($extraKeys as $key) {
                    $hint = \Packetery\Nette\Utils\ObjectHelpers::getSuggestion($keys, (string) $key);
                    $context->addError('Unexpected item %path%' . ($hint ? ", did you mean '%hint%'?" : '.'), \Packetery\Nette\Schema\Message::UNEXPECTED_ITEM, ['hint' => $hint])->path[] = $key;
                }
            }
        }
        foreach ($items as $itemKey => $itemVal) {
            $context->path[] = $itemKey;
            if (\array_key_exists($itemKey, $value)) {
                $value[$itemKey] = $itemVal->complete($value[$itemKey], $context);
            } else {
                $default = $itemVal->completeDefault($context);
                // checks required item
                if (!$context->skipDefaults && !$this->skipDefaults) {
                    $value[$itemKey] = $default;
                }
            }
            \array_pop($context->path);
        }
        if (\count($context->errors) > $errCount) {
            return;
        }
        return $this->doFinalize($value, $context);
    }
    public function completeDefault(Context $context)
    {
        return $this->required ? $this->complete([], $context) : null;
    }
}

```
