Boxable   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 1
dl 0
loc 113
ccs 41
cts 41
cp 1
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
B __destruct() 0 25 5
A getTranslatedType() 0 18 3
B box() 0 31 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Tdn\PhpTypes\Type\Traits;
6
7
use Tdn\PhpTypes\Memory\Memory;
8
use Tdn\PhpTypes\Type\Type;
9
10
/**
11
 * Trait Boxable.
12
 */
13
trait Boxable
14
{
15
    /**
16
     * Address to pointer in Memory::collection.
17
     *
18
     * @var string
19
     */
20
    private $memoryAddress = null;
21
22
    /**
23
     * Boxes a variable to a specific type, including future reassignment as a primitive.
24
     * Optionally takes value or instance of the variable.
25
     * If more than one argument should be passed to constructor, then an instance should be passed explicitly instead
26
     * of a primitive for $value argument.
27
     *
28
     * For examples please view the example.php file.
29
     *
30
     * @param null  &$pointer Anmpty variable to box (the pointer)
31
     * @param mixed $value    the primitive value to pass the constructor OR an instance of the type
32
     *
33
     * @throws \LogicException when the pointer has previously been declared
34
     * @throws \LogicException when the pointer has previously been declared
35
     * @throws \TypeError      when an invalid argument is passed as value or assigned to pointer
36
     */
37 58
    final public static function box(&$pointer, $value = null)
38
    {
39 58
        if ($pointer !== null) {
40 6
            throw new \LogicException(
41 6
                sprintf(
42
                    'The identifier of type %s is defined more than once. '.
43 6
                    'First argument of %s() must be null or undefined.',
44 6
                    gettype($pointer),
45 6
                    __METHOD__
46
                )
47
            );
48
        }
49
50
        try {
51 52
            if ($value instanceof static) {
52 9
                $pointer = clone $value;
53
            } else {
54 52
                $pointer = $value !== null ? new static($value) : new static();
55
            }
56 17
        } catch (\TypeError $e) {
57 17
            $message = sprintf(
58 17
                '%s. Argument can be instance of %s or scalar equivalent.',
59 17
                $e->getMessage(),
60 17
                static::class
61
            );
62
63 17
            throw new \TypeError($message, $e->getCode(), $e);
64
        }
65
66 41
        $pointer->memoryAddress = Memory::getNewAddress($pointer);
67 41
    }
68
69
    /**
70
     * Runs when a variable is reassigned or destroyed with $pointer = null;.
71
     * Basically overloads the assignment operator when a specific pointer has been boxed to return a new instance
72
     * of the previous type with the new assigned value.
73
     */
74 148
    final public function __destruct()
75
    {
76 148
        if ($this->memoryAddress === null) {
77 141
            return;
78
        }
79
80 18
        $pointer = &Memory::getPointer($this->memoryAddress);
81 18
        $value = $pointer;
82
83 18
        if ($value !== $this && $value !== null) {
84
            try {
85
                // Clear pointer before attempting to box new value.
86 18
                $pointer = null;
87 18
                static::box($pointer, $value);
88 6
            } catch (\TypeError $e) {
89
                // Reset the pointer to the previous value and re throw exception.
90
                // This will allow the variable to remain boxed, the exception to be caught, and handled appropriately.
91 6
                $pointer = clone $this;
92
93 6
                throw $e;
94
            }
95
        }
96
97 12
        Memory::shouldFree($this->memoryAddress);
98 12
    }
99
100
    /**
101
     * Translates type to cast front int to string representation.
102
     *
103
     * @param int|null $type
104
     *
105
     * @return string
106
     */
107 21
    protected function getTranslatedType(int $type = null): string
108
    {
109
        $supportedTypes = [
110 21
            Type::STRING => 'string',
111 21
            Type::BOOL => 'bool',
112 21
            Type::INT => 'int',
113 21
            Type::FLOAT => 'float',
114 21
            Type::ARRAY => 'array',
115
        ];
116
117 21
        if ($type !== null && !array_key_exists($type, $supportedTypes)) {
118 6
            throw new \OutOfBoundsException(
119 6
                sprintf('Type %s not found. Valid types are %s.', $type, implode(', ', $supportedTypes))
120
            );
121
        }
122
123 15
        return $supportedTypes[$type];
124
    }
125
}
126