TypedCDataDefaultImplementationTrait::newCData()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
cc 1
eloc 8
c 3
b 0
f 2
nc 1
nop 0
dl 0
loc 14
rs 10
1
<?php
2
3
/**
4
 * This file is part of the sj-i/typed-cdata package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace TypedCData;
15
16
use FFI;
17
use FFI\CData;
18
use PhpDocTypeReader\Context\IdentifierContextFactory;
0 ignored issues
show
Bug introduced by
The type PhpDocTypeReader\Context\IdentifierContextFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use PhpDocTypeReader\PhpDocTypeReader;
0 ignored issues
show
Bug introduced by
The type PhpDocTypeReader\PhpDocTypeReader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use PhpDocTypeReader\Type\GenericType;
0 ignored issues
show
Bug introduced by
The type PhpDocTypeReader\Type\GenericType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use PhpDocTypeReader\Type\ObjectType;
0 ignored issues
show
Bug introduced by
The type PhpDocTypeReader\Type\ObjectType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use ReflectionProperty;
23
24
trait TypedCDataDefaultImplementationTrait
25
{
26
    /** @return static */
27
    public static function fromCData(CData $cdata): self
28
    {
29
        $class = new \ReflectionClass(self::class);
30
        $self = new self();
31
        foreach ($class->getProperties() as $property) {
32
            $name = $property->getName();
33
            $type = $property->getType();
34
            if (is_null($type)) {
35
                throw new \LogicException('property of TypedCData must have types');
36
            }
37
            assert($type instanceof \ReflectionNamedType);
38
            $type_name = $type->getName();
39
            if (is_a($type_name, TypedCDataInterface::class, true)) {
40
                /** @var CData $original_cdata */
41
                $original_cdata = $cdata->$name;
42
                $self->$name = $type_name::fromCData($original_cdata);
43
            } elseif (is_a($type_name, TypedCDataArray::class, true)) {
44
                /** @var \FFI\CDataArray $original_cdata */
45
                $original_cdata = $cdata->$name;
46
                $self->$name = $type_name::fromCData($original_cdata, self::getElementTypeOfCDataArray($property));
47
            } else {
48
                $self->$name = $cdata->$name;
49
            }
50
        }
51
        return $self;
52
    }
53
54
    /**
55
     * @return class-string<TypedCDataInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TypedCDataInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TypedCDataInterface>.
Loading history...
56
     */
57
    private static function getElementTypeOfCDataArray(ReflectionProperty $property): string
58
    {
59
        $doc_comment = $property->getDocComment();
60
        if (!is_string($doc_comment)) {
0 ignored issues
show
introduced by
The condition is_string($doc_comment) is always true.
Loading history...
61
            throw new \LogicException('TypedCDataArray property needs type annotation');
62
        }
63
        $php_doc_type_reader = new PhpDocTypeReader();
64
        $identifier_context_factory = new IdentifierContextFactory();
65
        $identifier_context = $identifier_context_factory->createFromFile(
66
            $property->getDeclaringClass()->getFileName()
67
        );
68
        $type = $php_doc_type_reader->getVarTypes($doc_comment, $identifier_context);
69
        if (!($type instanceof GenericType)) {
70
            throw new \LogicException('TypedCDataArray property must have a generic type annotation');
71
        }
72
        if (count($type->parameter_types) !== 1) {
73
            throw new \LogicException('TypedCDataArray property must have one parameter type');
74
        }
75
        $parameter_type = $type->parameter_types[0];
76
        if (!($parameter_type instanceof ObjectType)) {
77
            throw new \LogicException('parameter type of TypedCDataArray property must be a object type');
78
        }
79
        $element_type = $parameter_type->class_name;
80
        if (!is_a($element_type, TypedCDataInterface::class, true)) {
81
            throw new \LogicException(
82
                'parameter type of TypedCDataArray property must be a TypedCDataInterface'
83
            );
84
        }
85
        return $element_type;
86
    }
87
88
    abstract public static function getCTypeName(): string;
89
90
    public function toCData(CData $cdata): CData
91
    {
92
        /**
93
         * @psalm-suppress RawObjectIteration
94
         * @var string $key
95
         * @var mixed $value
96
         */
97
        foreach ($this as $key => $value) {
98
            if (is_a($value, TypedCDataInterface::class)) {
99
                /** @psalm-suppress MixedAssignment */
100
                $cdata_property = $cdata->$key;
101
                if (!($cdata_property instanceof CData)) {
102
                    throw new \LogicException(
103
                        'TypedCData must correspond to a CData'
104
                    );
105
                }
106
                $cdata->$key = $value->toCData($cdata_property);
107
            } elseif (is_a($value, TypedCDataArray::class)) {
108
                /** @psalm-suppress MixedAssignment */
109
                $cdata_array_property = $cdata->$key;
110
                if (!($cdata_array_property instanceof CData)) {
111
                    throw new \LogicException(
112
                        'TypedCDataArray must correspond to a CData'
113
                    );
114
                }
115
                /** @var \FFI\CDataArray $cdata_array_property */
116
                $cdata->$key = $value->toCData($cdata_array_property);
117
            } else {
118
                $cdata->$key = $this->$key;
119
            }
120
        }
121
        return $cdata;
122
    }
123
124
    public static function newCData(): CData
125
    {
126
        $typename = static::getCTypeName();
127
        /** @var CData $cdata */
128
        $cdata = static::getFFI()->new($typename);
129
        /** @var FFI\CType $type */
130
        $type = static::getFFI()->type(
131
            $typename
132
        );
133
        $size = static::getFFI()::sizeof(
134
            $type
135
        );
136
        static::getFFI()::memset($cdata, 0, $size);
137
        return $cdata;
138
    }
139
140
    abstract protected static function getFFI(): FFI;
141
}
142