3v4l.org

run code in 300+ PHP versions simultaneously
<?php /** * @property string $pathSeparator * @property-read AddressableTree|null $rootNode */ class AddressableTree implements ArrayAccess, RecursiveIterator { /** * @var string */ private $pathSeparator; /** * @var AddressableTree */ private $rootNode; /** * @var array */ private $data = []; /** * @var self[] */ private $branches = []; /** * @param array $data * @param string $pathSeparator */ public function __construct(array $data = [], $pathSeparator = '/') { $this->setRootNode(null); $this->setPathSeparator($pathSeparator); foreach ($data as $key => $value) { $this->setElementAtKey($key, is_array($value) ? $this->createBranch($value) : $value, true); } } /** * @param string $name * @return mixed */ public function __get($name) { if (!in_array($name, ['pathSeparator', 'rootNode'])) { throw new \LogicException('Read of undefined property: ' . $name); } return $this->$name; } /** * @param string $name * @param mixed $value */ public function __set($name, $value) { if (!in_array($name, ['pathSeparator', 'rootNode'])) { throw new \LogicException('Write of undefined property: ' . $name); } call_user_func([$this, 'set' . $name], $value); } /** * @param string $value */ private function setPathSeparator($value) { $value = (string)$value; if ($this->rootNode && $value !== $this->rootNode->pathSeparator) { throw new \LogicException('Path separator can only be set on the root node'); } $this->pathSeparator = $value; foreach ($this->branches as $branch) { $branch->pathSeparator = $value; } } /** * @param AddressableTree|null $value */ private function setRootNode(AddressableTree $value = null) { $this->rootNode = $value; $branchRoot = $value ?: $this; foreach ($this->branches as $branch) { $branch->rootNode = $branchRoot; } } /** * @param array $value * @return AddressableTree */ private function createBranch(array $value = []) { $branch = new self($value, $this->pathSeparator); $branch->rootNode = $this->rootNode ?: $this; return $branch; } /** * @param $address * @return array|bool */ private function parseAddressParts($address) { if (false === $pos = strpos($address, $this->pathSeparator)) { return false; } else if (!$pos && !$pos = strpos($address, $this->pathSeparator, strlen($this->pathSeparator))) { return false; } return [substr($address, 0, $pos), substr($address, $pos + strlen($this->pathSeparator))]; } /** * @param string $key * @param mixed $value * @param bool $forceBranch */ private function setElementAtKey($key, $value, $forceBranch = false) { if ($value instanceof self) { if ($value->rootNode && !$forceBranch) { throw new \LogicException('Cannot add branch to tree: already attached to a tree'); } $value->rootNode = $this->rootNode ?: $this; $value->pathSeparator = $this->pathSeparator; if (isset($this->data[$key])) { $this->removeElementAtKey($key); } $this->data[$key] = $value; $this->branches[$key] = $value; } else { $this->data[$key] = $value; } } /** * @param string $key */ private function removeElementAtKey($key) { if (!array_key_exists($key, $this->data)) { throw new \LogicException("Cannot remove element at '$key': does not exist"); } if ($this->data[$key] instanceof self) { $this->data[$key]->rootNode = null; unset($this->branches[$key]); } unset($this->data[$key]); } /** * @param string $address * @param mixed $value */ public function setElementAtAddress($address, $value) { if (!$parts = $this->parseAddressParts($address)) { $this->setElementAtKey($address, $value); return; } list($key, $subAddress) = $parts; if (!array_key_exists($key, $this->data)) { $this->data[$key] = $this->branches[$key] = $this->createBranch(); } else if (!($this->data[$key] instanceof self)) { throw new \InvalidArgumentException("Target element address invalid: treats leaf '{$key}' as a branch"); } $this->branches[$key]->setElementAtAddress($subAddress, $value); } /** * @param string $address * @return mixed */ public function getElementAtAddress($address) { if (!$parts = $this->parseAddressParts($address)) { if (!array_key_exists($address, $this->data)) { throw new \InvalidArgumentException("Target leaf address invalid: element '{$address}' does not exist"); } return $this->data[$address]; } list($key, $subAddress) = $parts; if (!array_key_exists($key, $this->data)) { throw new \InvalidArgumentException("Target element address invalid: branch '{$key}' does not exist"); } else if (!($this->data[$key] instanceof self)) { throw new \InvalidArgumentException("Target element address invalid: treats leaf '{$key}' as a branch"); } return $this->branches[$key]->getElementAtAddress($subAddress); } /** * @param string $address */ public function removeElementAtAddress($address) { if (!$parts = $this->parseAddressParts($address)) { $this->removeElementAtKey($address); return; } list($key, $subAddress) = $parts; if (!array_key_exists($key, $this->data)) { throw new \InvalidArgumentException("Target element address invalid: branch '{$key}' does not exist"); } else if (!($this->data[$key] instanceof self)) { throw new \InvalidArgumentException("Target element address invalid: treats leaf '{$key}' as a branch"); } $this->branches[$key]->removeElementAtAddress($subAddress); } /** * @param string $address * @return bool */ public function addressExists($address) { if (!$parts = $this->parseAddressParts($address)) { return array_key_exists($address, $this->data); } list($key, $subAddress) = $parts; if (!array_key_exists($key, $this->data) || !($this->data[$key] instanceof self)) { return false; } return $this->branches[$key]->addressExists($subAddress); } /** * @return mixed */ public function current() { return current($this->data); } public function next() { next($this->data); } /** * @return string */ public function key() { return key($this->data); } /** * @return bool */ public function valid() { return key($this->data) !== null; } public function rewind() { reset($this->data); } /** * @return bool */ public function hasChildren() { return current($this->data) instanceof self; } /** * @return RecursiveIterator|null */ public function getChildren() { return current($this->data) instanceof self ? current($this->data) : null; } /** * @param string $address * @return bool */ public function offsetExists($address) { return $this->addressExists($address); } /** * @param string $address * @return mixed */ public function offsetGet($address) { return $this->getElementAtAddress($address); } /** * @param string $address * @param mixed $value */ public function offsetSet($address, $value) { $this->setElementAtAddress($address, $value); } /** * @param string $key */ public function offsetUnset($key) { $this->removeElementAtAddress($key); } } $tree = new AddressableTree([ 'foo' => [ 'bar' => 1, 'baz' => 2, ], 'qux' => [ 'yo' => ['mama' => ['so' => 'fat']] ], 'stuff' => 'ting', ]); var_dump($tree); /* var_dump($tree['foo/bar'], $tree['/foo/bar'], $tree['foo']['bar']); $tree['/yo/mama/so'] = 'ugly'; var_dump($tree);

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).

VersionSystem time (s)User time (s)Memory (MiB)
8.3.40.0110.00718.80
8.3.30.0100.00319.04
8.3.20.0040.00420.37
8.3.10.0080.00821.82
8.3.00.0080.00019.25
8.2.170.0120.00922.96
8.2.160.0130.00320.52
8.2.150.0060.00324.18
8.2.140.0040.00424.66
8.2.130.0000.00726.16
8.2.120.0000.00822.10
8.2.110.0060.00322.05
8.2.100.0090.00317.63
8.2.90.0030.00619.09
8.2.80.0030.00617.97
8.2.70.0040.00417.50
8.2.60.0040.00417.91
8.2.50.0040.00418.07
8.2.40.0070.00318.09
8.2.30.0040.00417.85
8.2.20.0040.00417.47
8.2.10.0040.00417.87
8.2.00.0040.00417.57
8.1.270.0040.00422.07
8.1.260.0080.00026.35
8.1.250.0040.00428.09
8.1.240.0030.00623.90
8.1.230.0060.00617.42
8.1.220.0040.00417.74
8.1.210.0040.00418.77
8.1.200.0030.00617.23
8.1.190.0030.00617.10
8.1.180.0000.00818.10
8.1.170.0040.00418.61
8.1.160.0000.00721.98
8.1.150.0070.00018.84
8.1.140.0000.00717.33
8.1.130.0000.00717.80
8.1.120.0000.00717.41
8.1.110.0050.00217.42
8.1.100.0040.00417.33
8.1.90.0000.00717.38
8.1.80.0040.00417.29
8.1.70.0000.00717.34
8.1.60.0050.00317.53
8.1.50.0040.00417.29
8.1.40.0060.00317.45
8.1.30.0060.00317.48
8.1.20.0040.00417.47
8.1.10.0030.00517.36
8.1.00.0000.00717.46
8.0.300.0020.00518.77
8.0.290.0000.00716.75
8.0.280.0000.00718.34
8.0.270.0070.00017.26
8.0.260.0070.00017.12
8.0.250.0070.00016.88
8.0.240.0070.00016.99
8.0.230.0000.00716.87
8.0.220.0050.00316.91
8.0.210.0030.00316.84
8.0.200.0030.00316.95
8.0.190.0000.00716.90
8.0.180.0040.00416.96
8.0.170.0000.00716.92
8.0.160.0000.00817.05
8.0.150.0030.00516.84
8.0.140.0040.00416.89
8.0.130.0000.00613.35
8.0.120.0040.00416.93
8.0.110.0000.00816.82
8.0.100.0000.00716.96
8.0.90.0040.00416.86
8.0.80.0090.01216.99
8.0.70.0000.00716.73
8.0.60.0000.00716.99
8.0.50.0040.00416.94
8.0.30.0110.01216.94
8.0.20.0120.01317.40
8.0.10.0000.00717.02
8.0.00.0100.01116.61
7.4.330.0000.00515.02
7.4.320.0040.00416.67
7.4.300.0000.00716.72
7.4.290.0000.00716.72
7.4.280.0030.00716.59
7.4.270.0070.00016.64
7.4.260.0000.00816.60
7.4.250.0050.00316.48
7.4.240.0050.00316.66
7.4.230.0040.00416.42
7.4.220.0130.01616.62
7.4.210.0090.01216.58
7.4.200.0000.00716.46
7.4.160.0180.00916.54
7.4.150.0030.01717.40
7.4.140.0160.00917.86
7.4.130.0150.00816.74
7.4.120.0090.01416.81
7.4.110.0140.00716.60
7.4.100.0160.00616.54
7.4.90.0090.00916.77
7.4.80.0140.00719.39
7.4.70.0130.00716.65
7.4.60.0040.01516.66
7.4.50.0000.00816.54
7.4.40.0030.01716.68
7.4.30.0070.01116.70
7.4.00.0120.00614.89
7.3.330.0030.00313.46
7.3.320.0030.00313.41
7.3.310.0050.00216.52
7.3.300.0000.00716.36
7.3.290.0090.00616.51
7.3.280.0120.00516.56
7.3.270.0170.01217.40
7.3.260.0050.01616.68
7.3.250.0140.00716.54
7.3.240.0090.01216.48
7.3.230.0120.00916.72
7.3.210.0060.01516.60
7.3.200.0100.01619.39
7.3.190.0110.01116.57
7.3.180.0090.01616.61
7.3.170.0120.01016.75
7.3.160.0000.02016.61
7.3.120.0040.01815.02
7.3.110.0130.00715.03
7.3.100.0120.00814.90
7.3.90.0060.00915.09
7.3.80.0030.01315.25
7.3.70.0090.00914.99
7.3.60.0060.01014.72
7.3.50.0040.01114.77
7.3.40.0030.01015.00
7.3.30.0030.01315.00
7.3.20.0120.00616.71
7.3.10.0100.00916.58
7.3.00.0060.01516.63
7.2.330.0110.01116.84
7.2.320.0130.01316.81
7.2.310.0030.01716.96
7.2.300.0070.01516.83
7.2.290.0070.01316.90
7.2.250.0100.00715.03
7.2.240.0100.01315.09
7.2.230.0040.01414.93
7.2.220.0040.01114.97
7.2.210.0030.01315.07
7.2.200.0060.01215.04
7.2.190.0060.00915.29
7.2.180.0030.00715.33
7.2.170.0040.01115.13
7.2.130.0240.00616.57
7.2.120.0160.00616.33
7.2.110.0200.00316.52
7.2.100.0090.01516.51
7.2.90.0160.01216.21
7.2.80.0140.00416.53
7.2.70.0060.01516.72
7.2.60.0130.00516.58
7.2.50.0160.00816.44
7.2.40.0150.01116.56
7.2.30.0170.00316.42
7.2.20.0100.01316.50
7.2.10.0090.01816.45
7.2.00.0100.00917.92
7.1.330.0030.01415.76
7.1.320.0030.01015.55
7.1.310.0070.01115.55
7.1.300.0100.01015.86
7.1.290.0000.01215.62
7.1.280.0060.00615.56
7.1.270.0040.01115.55
7.1.260.0070.01115.90
7.1.250.0140.00615.45
7.1.200.0060.00616.01
7.1.100.0030.01518.29
7.1.70.0040.00817.38
7.1.60.0070.02019.11
7.1.50.0030.01717.01
7.1.00.0000.08022.44
7.0.200.0000.00816.94
7.0.140.0070.07321.99
7.0.100.0230.06320.03
7.0.90.0100.08320.12
7.0.80.0100.08320.04
7.0.70.0000.08720.26
7.0.60.0270.06720.21
7.0.50.0200.07020.41
7.0.40.0030.07020.18
7.0.30.0070.07320.04
7.0.20.0100.07720.11
7.0.10.0100.07320.14
7.0.00.0100.04720.07
5.6.280.0000.07721.21
5.6.250.0030.06720.73
5.6.240.0070.07320.54
5.6.230.0030.08720.73
5.6.220.0100.04320.71
5.6.210.0030.09320.62
5.6.200.0130.07721.13
5.6.190.0100.08021.09
5.6.180.0170.08321.23
5.6.170.0030.08321.07
5.6.160.0070.04321.09
5.6.150.0170.03021.00
5.6.140.0070.08321.07
5.6.130.0100.07021.06
5.6.120.0130.03720.96
5.6.110.0030.06021.16
5.6.100.0170.04721.08
5.6.90.0170.06321.05
5.6.80.0170.03720.62
5.6.70.0000.05020.43
5.6.60.0030.08320.38
5.6.50.0130.04020.40
5.6.40.0030.08020.44
5.6.30.0130.06020.56
5.6.20.0070.04720.46
5.6.10.0070.06320.43
5.6.00.0130.07720.43
5.5.380.0100.03720.48
5.5.370.0130.07320.58
5.5.360.0070.08320.41
5.5.350.0070.08320.54
5.5.340.0030.06320.94
5.5.330.0130.08020.82
5.5.320.0100.06320.89
5.5.310.0100.08020.86
5.5.300.0030.07020.99
5.5.290.0030.06720.80
5.5.280.0230.07020.84
5.5.270.0130.07020.89
5.5.260.0170.06720.98
5.5.250.0100.08020.52
5.5.240.0100.04020.35
5.5.230.0000.09020.33
5.5.220.0170.05020.35
5.5.210.0130.03320.20
5.5.200.0030.04020.15
5.5.190.0030.05020.34
5.5.180.0070.06720.32
5.5.160.0100.08020.03
5.5.150.0100.07320.32
5.5.140.0170.05720.31
5.5.130.0130.06720.19
5.5.120.0070.05720.28
5.5.110.0130.05720.15
5.5.100.0070.07020.24
5.5.90.0100.04020.13
5.5.80.0070.08020.13
5.5.70.0130.06720.15
5.5.60.0070.05720.21
5.5.50.0100.05020.21
5.5.40.0170.07720.17
5.5.30.0070.08020.06
5.5.20.0070.07720.11
5.5.10.0130.05720.15
5.5.00.0200.04020.17
5.4.450.0070.08319.38
5.4.440.0070.06319.48
5.4.430.0100.06019.37
5.4.420.0100.07019.46
5.4.410.0070.05019.29
5.4.400.0070.03719.24
5.4.390.0130.07719.09
5.4.380.0030.07719.23
5.4.370.0100.07019.18
5.4.360.0070.08018.91
5.4.350.0100.04718.90
5.4.340.0070.04318.85
5.4.320.0100.07019.12
5.4.310.0200.06718.85
5.4.300.0030.07019.02
5.4.290.0130.07319.14
5.4.280.0030.09019.04
5.4.270.0070.07019.04
5.4.260.0130.07318.88
5.4.250.0100.07319.20
5.4.240.0100.07019.03
5.4.230.0130.07019.04
5.4.220.0170.06719.04
5.4.210.0070.04019.03
5.4.200.0100.06018.84
5.4.190.0100.07019.23
5.4.180.0000.07319.15
5.4.170.0070.04019.04
5.4.160.0100.05719.10
5.4.150.0100.06719.01
5.4.140.0130.06316.51
5.4.130.0070.06316.35
5.4.120.0070.05016.50
5.4.110.0070.04716.40
5.4.100.0030.07316.52
5.4.90.0200.06016.33
5.4.80.0170.06016.54
5.4.70.0000.04016.55
5.4.60.0100.07716.50
5.4.50.0100.05016.50
5.4.40.0100.07316.53
5.4.30.0100.07016.53
5.4.20.0030.07316.34
5.4.10.0030.08016.47
5.4.00.0130.05715.94

preferences:
43.49 ms | 400 KiB | 5 Q