• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

diego-ninja / granite / 22631927117

03 Mar 2026 04:13PM UTC coverage: 83.371% (+0.3%) from 83.117%
22631927117

push

github

web-flow
Merge pull request #20 from diego-ninja/feature/performance-optimizations

Performance optimizations & README reorganization

172 of 191 new or added lines in 7 files covered. (90.05%)

2 existing lines in 2 files now uncovered.

2973 of 3566 relevant lines covered (83.37%)

16.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.16
/src/Traits/HasSerialization.php
1
<?php
2

3
namespace Ninja\Granite\Traits;
4

5
use BackedEnum;
6
use DateTimeInterface;
7
use Ninja\Granite\Config\GraniteConfig;
8
use Ninja\Granite\Contracts\GraniteObject;
9
use Ninja\Granite\Exceptions\ReflectionException;
10
use Ninja\Granite\Exceptions\SerializationException;
11
use Ninja\Granite\Serialization\Attributes\DateTimeProvider;
12
use Ninja\Granite\Serialization\MetadataCache;
13
use Ninja\Granite\Serialization\SerializationCache;
14
use Ninja\Granite\Support\CarbonSupport;
15
use Ninja\Granite\Support\ReflectionCache;
16
use Ninja\Granite\Transformers\CarbonTransformer;
17
use ReflectionProperty;
18
use RuntimeException;
19
use UnitEnum;
20

21
/**
22
 * Trait providing serialization functionality for Granite objects.
23
 * Handles conversion of objects to arrays and JSON format.
24
 */
25
trait HasSerialization
26
{
27
    /**
28
     * Get Carbon transformer from property attributes.
29
     * This method will be provided by HasCarbonSupport trait.
30
     *
31
     * @param ReflectionProperty|null $property Property to check for attributes
32
     * @param DateTimeProvider|null $classProvider Class-level provider
33
     * @return CarbonTransformer|null Carbon transformer or null
34
     */
35
    abstract protected static function getCarbonTransformerFromAttributes(?ReflectionProperty $property = null, ?DateTimeProvider $classProvider = null): ?CarbonTransformer;
36

37
    /**
38
     * @return array Serialized array
39
     * @throws RuntimeException If a property cannot be serialized
40
     * @throws SerializationException|ReflectionException
41
     */
42
    public function array(): array
47✔
43
    {
44
        $cached = SerializationCache::get($this);
47✔
45
        if (null !== $cached) {
47✔
46
            return $cached;
5✔
47
        }
48

49
        $result = $this->computeArray();
47✔
50
        SerializationCache::set($this, $result);
46✔
51

52
        return $result;
46✔
53
    }
54

55
    /**
56
     * @throws SerializationException|ReflectionException
57
     */
58
    public function json(): string
6✔
59
    {
60
        $cached = SerializationCache::getJson($this);
6✔
61
        if (null !== $cached) {
6✔
NEW
62
            return $cached;
×
63
        }
64

65
        $json = json_encode($this->array());
6✔
66
        if (false === $json) {
6✔
67
            throw new RuntimeException('Failed to encode object to JSON');
×
68
        }
69

70
        SerializationCache::setJson($this, $json);
6✔
71

72
        return $json;
6✔
73
    }
74

75
    /**
76
     * Define custom property names for serialization.
77
     * Override in child classes to customize property names.
78
     *
79
     * @return array<string, string> Mapping of PHP property names to serialized names
80
     */
81
    protected static function serializedNames(): array
60✔
82
    {
83
        return [];
60✔
84
    }
85

86
    /**
87
     * Define properties that should be hidden during serialization.
88
     * Override in child classes to hide specific properties.
89
     *
90
     * @return array<string> List of property names to hide
91
     */
92
    protected static function hiddenProperties(): array
60✔
93
    {
94
        return [];
60✔
95
    }
96

97
    /**
98
     * @return array Serialized array
99
     * @throws SerializationException|ReflectionException
100
     */
101
    private function computeArray(): array
47✔
102
    {
103
        $profile = ReflectionCache::getClassProfile(static::class);
47✔
104
        if ($profile->canUseFastPath) {
47✔
105
            return $profile->toArray($this);
8✔
106
        }
107

108
        $result = [];
39✔
109
        $properties = ReflectionCache::getPublicProperties(static::class);
39✔
110
        $metadata = MetadataCache::getMetadata(static::class);
39✔
111

112
        foreach ($properties as $property) {
39✔
113
            $phpName = $property->getName();
39✔
114

115
            // Skip hidden properties
116
            if ($metadata->isHidden($phpName)) {
39✔
117
                continue;
6✔
118
            }
119

120
            // Skip uninitialized properties
121
            if ( ! $property->isInitialized($this)) {
39✔
122
                continue;
12✔
123
            }
124

125
            $value = $property->getValue($this);
39✔
126
            $serializedValue = $this->serializeValue($phpName, $value, $property);
39✔
127

128
            // Use custom property name if defined (includes convention-applied names)
129
            $serializedName = $metadata->getSerializedName($phpName);
38✔
130
            $result[$serializedName] = $serializedValue;
38✔
131
        }
132

133
        return $result;
38✔
134
    }
135

136
    /**
137
     * @param string $propertyName The property name (for error reporting)
138
     * @param mixed $value The value to serialize
139
     * @param ReflectionProperty|null $property Property for attribute access
140
     * @return mixed Serialized value
141
     * @throws SerializationException If the value cannot be serialized
142
     */
143
    private function serializeValue(string $propertyName, mixed $value, ?ReflectionProperty $property = null): mixed
39✔
144
    {
145
        if (null === $value) {
39✔
146
            return null;
1✔
147
        }
148

149
        if (is_scalar($value) || is_array($value)) {
39✔
150
            return $value;
35✔
151
        }
152

153
        // Handle Carbon instances with custom serialization
154
        if (CarbonSupport::isCarbonInstance($value)) {
7✔
155
            $carbonTransformer = self::getCarbonTransformerFromAttributes($property, null);
3✔
156
            if (null !== $carbonTransformer) {
3✔
157
                /** @var DateTimeInterface $value */
158
                return $carbonTransformer->serialize($value);
2✔
159
            }
160

161
            // Fallback to global config
162
            $config = GraniteConfig::getInstance();
2✔
163
            /** @var DateTimeInterface $value */
164
            return CarbonSupport::serialize(
2✔
165
                $value,
2✔
166
                $config->getCarbonSerializeFormat(),
2✔
167
                $config->getCarbonSerializeTimezone(),
2✔
168
            );
2✔
169
        }
170

171
        if ($value instanceof DateTimeInterface) {
4✔
172
            return $value->format(DateTimeInterface::ATOM);
1✔
173
        }
174

175
        if (interface_exists('UnitEnum') && $value instanceof UnitEnum) {
3✔
176
            if ($value instanceof BackedEnum) {
1✔
177
                return $value->value;
1✔
178
            }
179
            return $value->name;
×
180
        }
181

182
        if ($value instanceof GraniteObject) {
2✔
183
            return $value->array();
1✔
184
        }
185

186
        throw SerializationException::unsupportedType(static::class, $propertyName, get_debug_type($value));
1✔
187
    }
188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc