<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use ArrayAccess;
use ArrayIterator;
use Countable;
use FG\ASN1\Exception\ParserException;
use Iterator;
abstract class Construct extends ASNObject implements Countable, ArrayAccess, Iterator, Parsable
{
/** @var \FG\ASN1\ASNObject[] */
protected $children;
private $iteratorPosition;
/**
* @param \FG\ASN1\ASNObject[] $children the variadic type hint is commented due to https://github.com/facebook/hhvm/issues/4858
*/
public function __construct(/* HH_FIXME[4858]: variadic + strict */ ...$children)
{
$this->children = $children;
$this->iteratorPosition = 0;
}
public function getContent()
{
return $this->children;
}
#[\ReturnTypeWillChange]
public function rewind()
{
$this->iteratorPosition = 0;
}
#[\ReturnTypeWillChange]
public function current()
{
return $this->children[$this->iteratorPosition];
}
#[\ReturnTypeWillChange]
public function key()
{
return $this->iteratorPosition;
}
#[\ReturnTypeWillChange]
public function next()
{
$this->iteratorPosition++;
}
#[\ReturnTypeWillChange]
public function valid()
{
return isset($this->children[$this->iteratorPosition]);
}
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return array_key_exists($offset, $this->children);
}
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->children[$offset];
}
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if ($offset === null) {
$offset = count($this->children);
}
$this->children[$offset] = $value;
}
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->children[$offset]);
}
protected function calculateContentLength()
{
$length = 0;
foreach ($this->children as $component) {
$length += $component->getObjectLength();
}
return $length;
}
protected function getEncodedValue()
{
$result = '';
foreach ($this->children as $component) {
$result .= $component->getBinary();
}
return $result;
}
public function addChild(ASNObject $child)
{
$this->children[] = $child;
}
public function addChildren(array $children)
{
foreach ($children as $child) {
$this->addChild($child);
}
}
public function __toString()
{
$nrOfChildren = $this->getNumberOfChildren();
$childString = $nrOfChildren == 1 ? 'child' : 'children';
return "[{$nrOfChildren} {$childString}]";
}
public function getNumberOfChildren()
{
return count($this->children);
}
/**
* @return \FG\ASN1\ASNObject[]
*/
public function getChildren()
{
return $this->children;
}
/**
* @return \FG\ASN1\ASNObject
*/
public function getFirstChild()
{
return $this->children[0];
}
/**
* @param string $binaryData
* @param int $offsetIndex
*
* @throws Exception\ParserException
*
* @return Construct|static
*/
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
$parsedObject = new static();
self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
$startIndex = $offsetIndex;
$children = [];
$octetsToRead = $contentLength;
while ($octetsToRead > 0) {
$newChild = ASNObject::fromBinary($binaryData, $offsetIndex);
$octetsToRead -= $newChild->getObjectLength();
$children[] = $newChild;
}
if ($octetsToRead !== 0) {
throw new ParserException("Sequence length incorrect", $startIndex);
}
$parsedObject->addChildren($children);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
#[\ReturnTypeWillChange]
public function count($mode = COUNT_NORMAL)
{
return count($this->children, $mode);
}
public function getIterator()
{
return new ArrayIterator($this->children);
}
}