在我们的代码库中,我们有集合的概念。这些基于名为TypedArray的类,该类基于ArrayObject。
class ArrayObject extends \ArrayObject
{
public function __clone()
{
foreach ($this as $key => $value) {
$this[$key] = is_object($value) ? clone $value : $value;
}
}
public function insert(int $index, $element)
{
$data = $this->getArrayCopy();
if ($index < 0) {
$index = $this->count() + $index;
}
$data = array_merge(array_slice($data, 0, $index, true), [$element], array_slice($data, $index, null, true));
$this->exchangeArray($data);
}
public function splice(int $offset, int $length = null, $replacement = null)
{
$data = $this->getArrayCopy();
if (is_null($length) && is_null($replacement)) {
$result = array_splice($data, $offset);
} else {
$result = array_splice($data, $offset, $length, $replacement);
}
$this->exchangeArray($data);
return new static($result);
}
public function unshift($value): int
{
$data = $this->getArrayCopy();
$result = array_unshift($data, $value);
$this->exchangeArray($data);
return $result;
}
public function slice(int $offset, int $length = null, bool $preserveKeys = false)
{
return new static(array_slice($this->getArrayCopy(), $offset, $length, $preserveKeys));
}
public function sort($sortFlags = SORT_REGULAR)
{
$data = $this->getArrayCopy();
$result = sort($data, $sortFlags);
$this->exchangeArray($data);
return $result;
}
public function walk($callback, $userData = null)
{
$data = $this->getArrayCopy();
$result = array_walk($data, $callback, $userData);
$this->exchangeArray($data);
return $result;
}
public function chunk(int $size, bool $preserveKeys = false): ArrayObject
{
$data = $this->getArrayCopy();
$result = array_chunk($data, $size, $preserveKeys);
return new ArrayObject($result);
}
public function column($columnKey): array
{
$data = $this->getArrayCopy();
$result = array_column($data, $columnKey);
return $result;
}
public function map(callable $mapper): ArrayObject
{
$data = $this->getArrayCopy();
$result = array_map($mapper, $data);
return new self($result);
}
public function each(callable $callable)
{
foreach ($this as &$item) {
$callable($item);
}
unset($item);
}
public function at(int $index)
{
$this->validateIndex($index);
return $this[$index];
}
private function validateIndex(int $index)
{
$exists = $this->indexExists($index);
if (!$exists) {
throw new OutOfRangeException('Index out of bounds of collection');
}
}
public function indexExists(int $index)
{
if ($index < 0) {
throw new InvalidArgumentException('Index must be a non-negative integer');
}
return $index < $this->count();
}
public function find(callable $callback)
{
foreach ($this as $element) {
if ($callback($element)) {
return $element;
}
}
return null;
}
public function filter(callable $callback, int $flag = 0)
{
$data = $this->getArrayCopy();
$result = array_filter($data, $callback, $flag);
return new static($result);
}
public function first()
{
if ($this->count() === 0) {
throw new \OutOfBoundsException('Cannot get first element of empty Collection');
}
return reset($this);
}
public function last()
{
if ($this->count() === 0) {
throw new \OutOfBoundsException('Cannot get last element of empty Collection');
}
return end($this);
}
public function reverse(bool $preserveKeys = false)
{
return new static(array_reverse($this->getArrayCopy(), $preserveKeys));
}
public function keys(): array
{
return array_keys($this->getArrayCopy());
}
public function reduce(callable $callback, $initial = null)
{
return array_reduce($this->getArrayCopy(), $callback, $initial);
}
}
和
abstract class AbstractTypedArray extends ArrayObject
{
use TypeValidator;
const ARRAY_TYPE = null;
private $arrayType = null;
public function __construct($input = [], $flags = 0, $iteratorClass = ArrayIterator::class)
{
if (empty(static::ARRAY_TYPE)) {
throw new \RuntimeException(
sprintf(
'%s::ARRAY_TYPE must be set to an allowable type.',
get_called_class()
)
);
}
try {
$this->arrayType = $this->determineType(static::ARRAY_TYPE);
} catch (\Collections\Exceptions\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
if (!(is_array($input) || (is_object($input) && in_array(Traversable::class, class_implements($input))))) {
throw new InvalidArgumentException('$input must be an array or an object that implements \Traversable.');
}
parent::__construct([], $flags, $iteratorClass);
foreach ($input as $key => $value) {
$this[$key] = $value;
}
}
public function unshift($value): int
{
try {
$this->validateItem($value, $this->arrayType);
} catch (\Collections\Exceptions\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
return parent::unshift($value);
}
public function offsetSet($offset, $value)
{
try {
$this->validateItem($value, $this->arrayType);
} catch (\Collections\Exceptions\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
parent::offsetSet($offset, $value);
}
public function sort($sortFlags = SORT_REGULAR)
{
if (!in_array(SortableInterface::class, class_implements($this->arrayType))) {
throw new \RuntimeException(
sprintf(
"Cannot sort an array of '%s' as that class does not implement '%s'.",
$this->arrayType,
SortableInterface::class
)
);
}
$originalData = $this->getArrayCopy();
$sortableData = array_map(
function (SortableInterface $item) {
return $item->getSortValue();
},
$originalData
);
$result = asort($sortableData, $sortFlags);
$order = array_keys($sortableData);
uksort(
$originalData,
function ($key1, $key2) use ($order) {
return array_search($key1, $order) <=> array_search($key2, $order);
}
);
$this->exchangeArray($originalData);
return $result;
}
public function filter(callable $callback, int $flag = 0)
{
if ($flag == ARRAY_FILTER_USE_KEY) {
throw new InvalidArgumentException('Cannot filter solely by key. Use ARRAY_FILTER_USE_BOTH and amend your callback to receive $value and $key.');
}
return parent::filter($callback, $flag);
}
}
一个例子使用。
class PaymentChannelCollection extends AbstractTypedArray
{
const ARRAY_TYPE = PaymentChannel::class;
}
您现在可以输入提示 PaymentChannelCollection
并确保您有一个PaymentChannels集合(例如)。
一些代码可能会在我们的命名空间中调用异常。我认为danielgsims / php-collections中也有一个类型验证器(我们最初使用这些集合,但是围绕它们的灵活性存在一些问题-它们很好,但对我们而言不是-所以无论如何都可以看看它们!)。
@return User[]
在DocBlock