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

krakjoe / ort / 19174598641

07 Nov 2025 04:07PM UTC coverage: 92.332% (+0.4%) from 91.936%
19174598641

push

github

krakjoe
define f32/f64 when onnxruntime is loaded

12450 of 13484 relevant lines covered (92.33%)

299476.01 hits per line

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

94.04
/src/tensor.c
1
/*
2
  +----------------------------------------------------------------------+
3
  | ort                                                                  |
4
  +----------------------------------------------------------------------+
5
  | Copyright (c) Joe Watkins 2025                                       |
6
  +----------------------------------------------------------------------+
7
  | This source file is subject to version 3.01 of the PHP license,      |
8
  | that is bundled with this package in the file LICENSE, and is        |
9
  | available through the world-wide-web at the following url:           |
10
  | http://www.php.net/license/3_01.txt                                  |
11
  | If you did not receive a copy of the PHP license and are unable to   |
12
  | obtain it through the world-wide-web, please send a note to          |
13
  | license@php.net so we can mail you a copy immediately.               |
14
  +----------------------------------------------------------------------+
15
  | Author: krakjoe                                                      |
16
  +----------------------------------------------------------------------+
17
 */
18

19
#include "ort.h"
20

21
#include "alloc.h"
22
#include "tensor.h"
23
#include "generators.h"
24
#include "status.h"
25

26
#ifdef ZTS
27
static MUTEX_T php_ort_tensor_mutex;
28
#endif
29

30
static HashTable php_ort_tensors;
31

32
zend_class_entry *php_ort_tensor_interface_ce;
33
zend_class_entry *php_ort_tensor_persistent_ce;
34
zend_class_entry *php_ort_tensor_transient_ce;
35
zend_object_handlers php_ort_tensor_handlers;
36

37
// Recursive shape/data validator
38
static inline zend_bool php_ort_tensor_validate_next(ONNXTensorElementDataType type, zend_long rank, zend_long *dimensions, zval *node, zend_long depth) {
10,550,524✔
39
    if (Z_TYPE_P(node) == IS_OBJECT &&
10,550,524✔
40
            instanceof_function(
7,576,344✔
41
                Z_OBJCE_P(node), php_ort_generator_ce)) {
×
42
        return 1;
×
43
    }
44

45
    if (depth == rank) {
10,550,524✔
46
        // Leaf node — must match scalar type
47
        switch (type) {
10,283,322✔
48
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
49
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32:
50
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64:
51
                php_ort_status_flow(
3,264,904✔
52
                    !(Z_TYPE_P(node) == IS_DOUBLE || Z_TYPE_P(node) == IS_LONG),
53
                    return 0,
54
                    php_ort_status_tensor_invaliddata_ce,
55
                    "tensor leaf at depth %zd: expected float, got %s",
56
                    depth, zend_zval_type_name(node)
57
                );
58
                break;
869,920✔
59
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
60
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
61
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
62
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
63
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
64
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
65
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
66
                php_ort_status_flow(
6,993,360✔
67
                    (Z_TYPE_P(node) != IS_LONG),
68
                    return 0,
69
                    php_ort_status_tensor_invaliddata_ce,
70
                    "tensor leaf at depth %zd: expected integer, got %s",
71
                    depth, zend_zval_type_name(node)
72
                );
73
                break;
2,018,305✔
74
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64:
75
                php_ort_status_flow(!SUCCESS,
×
76
                    return 0,
77
                    php_ort_status_tensor_invalidtype_ce,
78
                    "UINT64 tensor type is not supported (values exceed PHP integer range)");
79
                break;
80
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
81
                php_ort_status_flow(
25,007✔
82
                    !(Z_TYPE_P(node) == IS_TRUE || Z_TYPE_P(node) == IS_FALSE),
83
                    return 0,
84
                    php_ort_status_tensor_invaliddata_ce,
85
                    "tensor leaf at depth %zd: expected bool, got %s",
86
                    depth, zend_zval_type_name(node)
87
                );
88
                break;
7,350✔
89
            default:
90
                php_ort_status_flow(!SUCCESS,
51✔
91
                    return 0,
92
                    php_ort_status_tensor_invalidtype_ce,
93
                    "unknown data type (%zd) provided",
94
                    (zend_long) type);
95
        }
96
        return 1;
2,895,575✔
97
    }
98

99
    php_ort_status_flow(
267,202✔
100
        (Z_TYPE_P(node) != IS_ARRAY),
101
        return 0,
102
        php_ort_status_tensor_invaliddata_ce,
103
        "tensor node at depth %zd: expected array, got %s",
104
        depth, zend_zval_type_name(node)
105
    );
106

107
    HashTable *ht = Z_ARRVAL_P(node);
267,202✔
108
    php_ort_status_flow(
267,202✔
109
        (depth >= rank),
110
        return 0,
111
        php_ort_status_tensor_invalidshape_ce,
112
        "validator exceeded tensor rank: depth %zd, rank %zd",
113
        depth, rank
114
    );
115
    php_ort_status_flow(
267,202✔
116
        (zend_hash_num_elements(ht) != dimensions[depth]),
117
        return 0,
118
        php_ort_status_tensor_invaliddata_ce,
119
        "ragged array: sub-array at dimension %zd has length %zd, expected %zd",
120
        depth, zend_hash_num_elements(ht), dimensions[depth]
121
    );
122

123
    for (zend_long i = 0; i < dimensions[depth]; i++) {
10,778,958✔
124
        zval *child = zend_hash_index_find(ht, i);
10,511,987✔
125
        php_ort_status_flow(
10,511,987✔
126
            (!child),
127
            return 0,
128
            php_ort_status_tensor_invalidshape_ce,
129
            "tensor node at depth %zd: missing element at index %zd",
130
            depth, i
131
        );
132
        if (!php_ort_tensor_validate_next(
10,511,987✔
133
                type, rank, dimensions, child, depth + 1)) {
2,962,875✔
134
            // The recursive call will emit its own exception with details
135
            return 0;
75✔
136
        }
137
    }
2,962,875✔
138

139
    return 1;
78,480✔
140
}
2,974,180✔
141

142
static zend_always_inline zend_bool php_ort_tensor_validate(zval *shape, zend_string *name, zval *data, ONNXTensorElementDataType type) {
34,784✔
143
    // Skip generator validation
144
    if (Z_TYPE_P(data) == IS_OBJECT && 
39,404✔
145
            instanceof_function(
28,354✔
146
                Z_OBJCE_P(data), php_ort_generator_ce)) {
510✔
147
        return 1;
510✔
148
    }
149

150
    zend_long dimensions[16];
38,894✔
151
    zend_long rank = 0;
38,894✔
152
    
153
    rank = zend_hash_num_elements(Z_ARRVAL_P(shape));
38,894✔
154
    
155
    // Handle scalar tensor case (rank 0, empty shape array)
156
    if (rank == 0) {
38,894✔
157
        // For scalar tensors, data must be a single-element array
158
        if (Z_TYPE_P(data) != IS_ARRAY) {
238✔
159
            php_ort_status_flow(!SUCCESS,
×
160
            {
161
                return 0;
162
            },
163
            php_ort_status_tensor_invaliddata_ce,
164
            "scalar tensor data must be provided as a single-element array");
165
        }
166
        
167
        // Check that the data array has exactly one element
168
        if (zend_hash_num_elements(Z_ARRVAL_P(data)) != 1) {
238✔
169
            php_ort_status_flow(!SUCCESS,
34✔
170
            {
171
                return 0;
172
            },
173
            php_ort_status_tensor_invaliddata_ce,
174
            "scalar tensor must have exactly one data element");
175
        }
176
        
177
        // Get the first (and only) element
178
        zval *scalar = zend_hash_index_find(Z_ARRVAL_P(data), 0);
204✔
179
        if (!scalar) {
204✔
180
            return 0;
181
        }
182
        
183
        // Validate the scalar element type
184
        switch (type) {
204✔
185
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
186
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32:
187
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64:
188
                return Z_TYPE_P(scalar) == IS_DOUBLE || Z_TYPE_P(scalar) == IS_LONG;
119✔
189
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
190
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
191
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
192
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
193
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
194
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
195
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
196
                return Z_TYPE_P(scalar) == IS_LONG;
51✔
197
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64:
198
                // UINT64 is not supported due to PHP's signed 64-bit integer limitation
199
                php_ort_status_flow(!SUCCESS,
×
200
                {
201
                    return 0;
202
                },
203
                php_ort_status_tensor_invalidtype_ce,
204
                "UINT64 tensor type is not supported (values exceed PHP integer range)");
205
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
206
                return Z_TYPE_P(scalar) == IS_TRUE || Z_TYPE_P(scalar) == IS_FALSE;
34✔
207
            
208
            default: php_ort_status_flow(!SUCCESS,
×
209
            {
210
                return 0;
211
            },
212
            php_ort_status_tensor_invalidtype_ce,
213
            "unknown data type (%zd) provided",
214
            (zend_long) type);
215
        }
216
    }
60✔
217

218
    php_ort_status_flow(
38,656✔
219
        (rank > 16),
220
        return 0,
221
        php_ort_status_tensor_invalidshape_ce,
222
        "invalid shape information (must not exceed 16 dimensions)");
223

224
    // Extract and validate dimensions
225
    zend_long index = 0;
38,656✔
226
    zval *next;
38,656✔
227
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), next) {
87,232✔
228
        php_ort_status_flow(
48,678✔
229
            (Z_TYPE_P(next) != IS_LONG || Z_LVAL_P(next) <= 0),
230
            return 0,
231
            php_ort_status_tensor_invalidshape_ce,
232
            "shape information must be an array of positive integers");
233

234
        php_ort_status_flow(
48,576✔
235
            (!zend_hash_index_exists(Z_ARRVAL_P(shape), index)),
236
            return 0,
237
            php_ort_status_tensor_invalidshape_ce,
238
            "shape must be a packed array, index %zd is missing",
239
            index);
240

241
        dimensions[index++] = Z_LVAL_P(next);
48,559✔
242
    } ZEND_HASH_FOREACH_END();
25,595✔
243

244
    return php_ort_tensor_validate_next(type, rank, dimensions, data, 0);
38,537✔
245
}
11,560✔
246

247
void php_ort_tensor_store(ONNXTensorElementDataType type, void* target, zval* node) {
10,325,091✔
248
    // Enter into generator objects
249
    if (Z_TYPE_P(node) == IS_OBJECT && 
10,325,091✔
250
            instanceof_function(
7,438,123✔
251
                Z_OBJCE_P(node), php_ort_generator_ce)) {
20,927✔
252
        php_ort_generator_invoke(
20,927✔
253
            node, type, target);
6,155✔
254
        return;
20,927✔
255
    }
256

257
    switch (type) {
10,304,164✔
258
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
259
            *(float16 *)target = Z_TYPE_P(node) == IS_DOUBLE
784,363✔
260
                ? (float16)ort_math_float16_from_float64(Z_DVAL_P(node))
23,256✔
261
                : (float16)ort_math_float16_from_float64((float64)Z_LVAL_P(node));
761,107✔
262
            break;
784,363✔
263

264
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32:
265
            *(float32 *)target = Z_TYPE_P(node) == IS_DOUBLE
1,339,163✔
266
                ? (float32)Z_DVAL_P(node)
194,538✔
267
                : (float32)Z_LVAL_P(node);
1,285,553✔
268
            break;
1,339,163✔
269

270
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64:
271
            *(float64 *)target = Z_TYPE_P(node) == IS_DOUBLE
1,149,521✔
272
                ? Z_DVAL_P(node)
3,450✔
273
                : (float64)Z_LVAL_P(node);
1,146,071✔
274
            break;
1,149,521✔
275

276
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
713,016✔
277
            *(int8_t *)target = (int8_t)Z_LVAL_P(node);
1,010,106✔
278
            break;
1,010,106✔
279

280
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
727,220✔
281
            *(int16_t *)target = (int16_t)Z_LVAL_P(node);
1,016,575✔
282
            break;
1,016,575✔
283

284
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
728,528✔
285
            *(int32_t *)target = (int32_t)Z_LVAL_P(node);
1,018,428✔
286
            break;
1,018,428✔
287

288
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
695,088✔
289
            *(int64_t *)target = (int64_t)Z_LVAL_P(node);
984,708✔
290
            break;
984,708✔
291

292
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
684,984✔
293
            *(uint8_t *)target = (uint8_t)Z_LVAL_P(node);
970,394✔
294
            break;
970,394✔
295

296
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
717,572✔
297
            *(uint16_t *)target = (uint16_t)Z_LVAL_P(node);
1,002,907✔
298
            break;
1,002,907✔
299

300
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
717,620✔
301
            *(uint32_t *)target = (uint32_t)Z_LVAL_P(node);
1,002,975✔
302
            break;
1,002,975✔
303

304
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64:
305
            // UINT64 is not supported - this should not be reached due to validation
306
            php_ort_status_throw(
×
307
                php_ort_status_tensor_invalidtype_ce,
308
                "UINT64 tensor type is not supported (values exceed PHP integer range)");
309
            return;
×
310

311
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
312
            *(uint8_t *)target = (Z_TYPE_P(node) == IS_TRUE) ? 1 : 0;
25,024✔
313
            break;
25,024✔
314

315
        default: 
316
            php_ort_status_throw(
×
317
                php_ort_status_tensor_invalidtype_ce,
318
                "unknown data type (%zd) provided",
319
                (zend_long) type);
320
    }
321
}
2,907,895✔
322

323
static zend_always_inline zval* php_ort_tensor_flatten_next(ort_tensor_t* tensor, zval* node, zend_long idx) {
9,347,777✔
324
    // Special case for generators
325
    if (Z_TYPE_P(node) == IS_OBJECT &&
10,535,651✔
326
            instanceof_function(
15,138,840✔
327
                Z_OBJCE_P(node), php_ort_generator_ce)) {
23,936✔
328
        return node;
7,040✔
329
    }
330

331
    // Special case for scalar tensor (0 dimensions)
332
    if (tensor->dimensions == 0) {
10,511,715✔
333
        // For scalar tensors, we expect the data to be a single-element array
334
        if (Z_TYPE_P(node) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(node)) != 1) {
204✔
335
            return NULL;
336
        }
337

338
        // Get the scalar value (first element of the array)
339
        return zend_hash_index_find(Z_ARRVAL_P(node), idx);
204✔
340
    }
341

342
    if (Z_TYPE_P(node) != IS_ARRAY) {
10,511,511✔
343
        return NULL;
344
    }
345

346
    return zend_hash_index_find(Z_ARRVAL_P(node), idx);
10,511,511✔
347
}
2,969,835✔
348

349
static inline zend_bool php_ort_tensor_flatten(ort_tensor_t* tensor, size_t *offset, size_t size, zval *node, size_t depth) {
10,574,494✔
350
    // Scalar tensors have 0 dimensions
351
    if (tensor->dimensions == 0) {        
10,574,494✔
352
        // Get the scalar value (first element of the array)
353
        zval *scalar = php_ort_tensor_flatten_next(tensor, node, 0);
238✔
354
        if (!scalar) {
238✔
355
            return 0;
×
356
        }
357

358
        void *target = (char *)tensor->data + ((*offset) * size);
238✔
359

360
        php_ort_tensor_store(
238✔
361
            tensor->type, target, scalar);
70✔
362
        
363
        (*offset)++;
238✔
364
        return 1;
238✔
365
    }
70✔
366

367
    if (depth == tensor->dimensions) {
10,574,256✔
368
        void *target = (char *)tensor->data + ((*offset) * size);
10,303,926✔
369

370
        php_ort_tensor_store(
10,303,926✔
371
            tensor->type, target, node);
2,901,670✔
372
        
373
        (*offset)++;
10,303,926✔
374
        return 1;
10,303,926✔
375
    }
2,901,670✔
376

377
    for (int64_t i = 0; i < tensor->shape[depth]; i++) {
10,805,743✔
378
        zval *child = php_ort_tensor_flatten_next(tensor, node, i);
10,535,413✔
379
        if (!php_ort_tensor_flatten(tensor, offset, size, child, depth + 1)) {
10,535,413✔
380
            return 0;
381
        }
382
    }
2,969,765✔
383

384
    return 1;
79,490✔
385
}
2,981,230✔
386

387
static zend_always_inline zend_bool php_ort_tensor_allocate_persistent(ort_tensor_t *tensor, zend_string *name, zval *shape, zval *data, ONNXTensorElementDataType type) {
1,305✔
388
    size_t i = 0, offset = 0;
1,475✔
389

390
    tensor->name       = php_ort_string_copy(name);
2,515✔
391
    tensor->dimensions = zend_hash_num_elements(Z_ARRVAL_P(shape));
1,475✔
392
    tensor->type       = type;
1,475✔
393
    tensor->owner      = PHP_ORT_OWN_HEAP;
1,475✔
394

395
    // Handle scalar tensor case (empty shape array)
396
    if (tensor->dimensions == 0) {
1,475✔
397
        tensor->shape    = NULL;  // No shape array needed for scalars
34✔
398
        tensor->elements = 1;     // A scalar has exactly one element
34✔
399
        tensor->data     = ort_alloc(php_ort_tensor_sizeof(tensor), 1);
58✔
400
        
401
        return php_ort_tensor_flatten(tensor, &offset, php_ort_tensor_sizeof(tensor), data, 0);
58✔
402
    }
403
    
404
    // Normal case for non-scalar tensors
405
    tensor->shape    = pemalloc(tensor->dimensions * sizeof(int64_t), 1);
1,441✔
406
    tensor->elements = 1;
1,441✔
407

408
    // Copy shape and compute total number of elements
409
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), zval *dim) {
4,115✔
410
        int64_t size = (int64_t)Z_LVAL_P(dim);
2,674✔
411
        tensor->shape[i++] = size;
2,674✔
412
        tensor->elements *= size;
2,674✔
413
    } ZEND_HASH_FOREACH_END();
1,215✔
414

415
    tensor->data = ort_alloc(
2,457✔
416
        php_ort_tensor_sizeof(tensor), tensor->elements);
425✔
417

418
    return php_ort_tensor_flatten(tensor, &offset, php_ort_tensor_sizeof(tensor), data, 0);
2,457✔
419
}
435✔
420

421
static zend_always_inline zend_bool php_ort_tensor_allocate_transient(ort_tensor_t *tensor, zval *shape, zval *data, ONNXTensorElementDataType type) {
33,194✔
422
    size_t i = 0, offset = 0;
37,606✔
423

424
    tensor->name       = NULL;
37,606✔
425
    tensor->dimensions = zend_hash_num_elements(Z_ARRVAL_P(shape));
37,606✔
426
    tensor->type       = type;
37,606✔
427
    tensor->owner      = PHP_ORT_OWN_ZEND;
37,606✔
428

429
    // Handle scalar tensor case (empty shape array)
430
    if (tensor->dimensions == 0) {
37,606✔
431
        tensor->shape    = NULL;  // No shape array needed for scalars
204✔
432
        tensor->elements = 1;     // A scalar has exactly one element
204✔
433
        tensor->data     = ort_alloc(1, php_ort_tensor_sizeof(tensor));
348✔
434
        
435
        return php_ort_tensor_flatten(tensor, &offset, php_ort_tensor_sizeof(tensor), data, 0);
348✔
436
    }
437
    
438
    // Normal case for non-scalar tensors
439
    tensor->shape    = ecalloc(tensor->dimensions, sizeof(int64_t));
37,402✔
440
    tensor->elements = 1;
37,402✔
441

442
    // Copy shape and compute total number of elements
443
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), zval *dim) {
83,865✔
444
        int64_t size = (int64_t)Z_LVAL_P(dim);
46,463✔
445
        tensor->shape[i++] = size;
46,463✔
446
        tensor->elements *= size;
46,463✔
447
    } ZEND_HASH_FOREACH_END();
24,605✔
448

449
    tensor->data = ort_alloc(
63,834✔
450
        php_ort_tensor_sizeof(tensor), tensor->elements);
10,970✔
451

452
    return php_ort_tensor_flatten(tensor, &offset, php_ort_tensor_sizeof(tensor), data, 0);
63,834✔
453
}
11,030✔
454

455
static void ort_tensor_free(ort_tensor_t *tensor) {
69,368✔
456
    zend_bool persistent = 
89,718✔
457
        (tensor->owner == PHP_ORT_OWN_HEAP) ? 1 : 0;
69,368✔
458

459
    if (tensor->shape) {
69,368✔
460
        pefree(tensor->shape, persistent);
68,110✔
461
    }
19,980✔
462

463
#ifdef HAVE_ONNXRUNTIME
464
    if (!tensor->parent && tensor->data && !tensor->value) {
61,234✔
465
        ort_free(tensor->data);
61,144✔
466
    }
20,320✔
467

468
    if (tensor->value) {
61,234✔
469
        api->ReleaseValue(tensor->value);
15✔
470
    }
5✔
471
#else
472
    if (!tensor->parent && tensor->data) {
8,134✔
473
        ort_free(tensor->data);
8,124✔
474
    }
475
#endif
476

477
    if (tensor->name && !persistent) {
69,368✔
478
        zend_string_free(tensor->name);
30,119✔
479
    }
8,835✔
480

481
    if (tensor->parent) {
69,368✔
482
        ort_tensor_release(tensor->parent);
85✔
483
    }
25✔
484

485
    pefree(tensor, persistent);
69,368✔
486
}
69,368✔
487

488
void ort_tensor_release(ort_tensor_t *tensor) {
71,829✔
489
    if (!tensor) {
71,829✔
490
        return;
255✔
491
    }
492

493
    if (php_ort_atomic_delref(&tensor->refcount) == 0){
70,962✔
494
        ort_tensor_free(tensor);
69,368✔
495
    }
20,350✔
496
}
21,075✔
497

498
static void php_ort_tensor_del(zval *zv) {
1,475✔
499
    ort_tensor_release(
1,475✔
500
        ((ort_tensor_t*)
501
            Z_PTR_P(zv)));
1,475✔
502
}
1,475✔
503

504
static zend_bool php_ort_tensor_construct_persistent(ort_tensor_t *tensor, zend_string *name, zval *shape, zval *data, ONNXTensorElementDataType type){
1,645✔
505
    if (!php_ort_tensor_validate(shape, name, data, type)) {
2,805✔
506
        return 0;
170✔
507
    }
508

509
    if (!php_ort_tensor_allocate_persistent(tensor, name, shape, data, type)) {
2,515✔
510
        return 0;
511
    }
512

513
    return 1;
435✔
514
}
485✔
515

516
static zend_bool php_ort_tensor_construct_transient(ort_tensor_t *tensor, zval *shape, zval *data, ONNXTensorElementDataType type){
37,759✔
517
    if (!php_ort_tensor_validate(shape, NULL, data, type)) {
64,443✔
518
        return 0;
153✔
519
    }
520

521
    if (!php_ort_tensor_allocate_transient(tensor, shape, data, type)) {
64,182✔
522
        return 0;
523
    }
524

525
    return 1;
11,030✔
526
}
11,075✔
527

528
#ifdef HAVE_ONNXRUNTIME
529
OrtValue* php_ort_tensor_value(php_ort_tensor_t* ort) {
30✔
530
    OrtMemoryInfo* mi;
30✔
531
    OrtValue* value = NULL;
30✔
532
    OrtStatus* status;
30✔
533

534
    php_ort_status_flow(
30✔
535
        (status = api->CreateCpuMemoryInfo(
536
            OrtArenaAllocator, OrtMemTypeDefault, &mi)),
537
        {
538
            api->ReleaseStatus(status);
539

540
            return NULL;
541
        },
542
        php_ort_status_tensor_invalidmemory_ce,
543
        "failed to allocate MemoryInfo* for Tensor conversion: %s",
544
        api->GetErrorMessage(status));
545

546
    php_ort_status_flow(
50✔
547
        (status = api->CreateTensorWithDataAsOrtValue(
548
            mi,
549
            ort->object->data,
550
            ort->object->elements * php_ort_tensor_sizeof(ort->object),
551
            ort->object->shape,
552
            ort->object->dimensions,
553
            ort->object->type,
554
            &value)),
555
        {
556
            api->ReleaseStatus(status);
557

558
            return NULL;
559
        },
560
        php_ort_status_tensor_invalidmemory_ce,
561
        "failed to allocate OrtValue* for Tensor conversion: %s",
562
        api->GetErrorMessage(status));
563

564
    api->ReleaseMemoryInfo(mi);
30✔
565

566
    return value;
30✔
567
}
10✔
568

569
ort_tensor_t* php_ort_tensor_object(OrtValue* value) {
15✔
570
    ort_tensor_t  *tensor = pecalloc(1, sizeof(ort_tensor_t), 0);
15✔
571

572
    tensor->refcount = 1;
15✔
573
    tensor->owner    = PHP_ORT_OWN_ZEND;
15✔
574
    tensor->value    = value;
15✔
575

576
    OrtTensorTypeAndShapeInfo* otsi;
15✔
577

578
    php_ort_status_flow(
15✔
579
        api->GetTensorTypeAndShape(value, &otsi), {
580
            ort_tensor_free(tensor);
581

582
            return NULL;
583
        },
584
        php_ort_status_tensor_invalidshape_ce,
585
        "failed to determine shape for OrtValue* conversion");
586

587
    php_ort_status_flow(
15✔
588
        api->GetTensorElementType(otsi, &tensor->type),
589
        {
590
            ort_tensor_free(tensor);
591

592
            return NULL;
593
        },
594
        php_ort_status_tensor_invalidshape_ce,
595
        "failed to determine type for OrtValue* conversion");
596

597
    php_ort_status_flow(
15✔
598
        api->GetDimensionsCount(otsi, &tensor->dimensions),
599
        {
600
            ort_tensor_free(tensor);
601

602
            return NULL;
603
        },
604
        php_ort_status_tensor_invalidshape_ce,
605
        "failed to determine dimension count for OrtValue* conversion");
606

607
    // For non-scalar tensors, allocate shape array and populate it
608
    if (tensor->dimensions > 0) {
15✔
609
        tensor->shape = pecalloc(tensor->dimensions, sizeof(int64_t), 0);
15✔
610

611
        php_ort_status_flow(
15✔
612
            api->GetDimensions(otsi, tensor->shape, tensor->dimensions),
613
            {
614
                ort_tensor_free(tensor);
615

616
                return NULL;
617
            },
618
            php_ort_status_tensor_invalidshape_ce,
619
            "failed to fetch dimensions for OrtValue* conversion");
620
    } else {
5✔
621
        // For scalar tensors, shape remains NULL
622
        tensor->shape = NULL;
623
    }
624

625
    php_ort_status_flow(
15✔
626
        api->GetTensorShapeElementCount(otsi, &tensor->elements),
627
        {
628
            ort_tensor_free(tensor);
629

630
            return NULL;
631
        },
632
        php_ort_status_tensor_invalidshape_ce,
633
        "failed to determine element count for OrtValue* conversion");
634

635
    php_ort_status_flow(
15✔
636
        api->GetTensorMutableData(value, &tensor->data),
637
        {
638
            ort_tensor_free(tensor);
639

640
            return NULL;
641
        },
642
        php_ort_status_tensor_invaliddata_ce,
643
        "failed to fetch data for OrtValue* conversion");
644

645
    api->ReleaseTensorTypeAndShapeInfo(otsi);
15✔
646

647
    return tensor;
15✔
648
}
5✔
649
#endif
650

651
static zend_always_inline size_t php_ort_tensor_indexof(ort_tensor_t *tensor, int64_t *coords) {
45✔
652
    // For scalar tensors, always return index 0
653
    if (tensor->dimensions == 0) {
51✔
654
        return 0;
655
    }
656
    
657
    size_t index = 0;
51✔
658
    size_t stride = 1;
51✔
659

660
    for (int64_t i = tensor->dimensions - 1; i >= 0; i--) {
153✔
661
        index += 
102✔
662
            coords[i] * stride;
102✔
663
        stride *= tensor->shape[i];
102✔
664
    }
30✔
665

666
    return index;
15✔
667
}
15✔
668

669
ZEND_BEGIN_ARG_INFO_EX(php_ort_tensor_persistent_construct_arginfo, 0, 0, 1)
670
    ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
671
    ZEND_ARG_TYPE_INFO(0, shape, IS_ARRAY, 0)
672
    ZEND_ARG_TYPE_MASK(0, data, MAY_BE_ARRAY | MAY_BE_OBJECT, NULL)
673
    ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
674
ZEND_END_ARG_INFO()
675

676
PHP_METHOD(ONNX_Tensor_Persistent, __construct)
1,713✔
677
{
678
    php_ort_tensor_t *ort = php_ort_tensor_fetch(Z_OBJ(EX(This)));
1,713✔
679

680
    zend_string *name;
1,713✔
681
    zval        *shape = NULL;
1,713✔
682
    zval        *data  = NULL;
1,713✔
683
    zend_long    type  = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64;
1,713✔
684

685
    ZEND_PARSE_PARAMETERS_START(1, 4);
1,713✔
686
        Z_PARAM_STR(name)
1,911✔
687
        Z_PARAM_OPTIONAL
1,713✔
688
        Z_PARAM_ARRAY(shape)
1,903✔
689
        Z_PARAM_ZVAL(data)
1,645✔
690
        Z_PARAM_LONG(type)
1,833✔
691
    ZEND_PARSE_PARAMETERS_END();
703✔
692

693
#ifdef ZTS
694
    php_ort_status_flow(
1,208✔
695
        tsrm_mutex_lock(php_ort_tensor_mutex) != SUCCESS,
696
        return,
697
        php_ort_status_safetyerror_ce,
698
        "it was not possible to acquire the tensor mutex, something is terribly wrong");
699
#endif
700

701
    if (!(ort->object = zend_hash_find_ptr(&php_ort_tensors, name))) {
2,921✔
702
#ifdef ZTS
703
        php_ort_status_flow(
1,184✔
704
            (!shape || !data),
705
            {
706
                php_ort_status_flow(
707
                    tsrm_mutex_unlock(php_ort_tensor_mutex) != SUCCESS,
708
                    return,
709
                    php_ort_status_safetyerror_ce,
710
                    "it was not possible to release the tensor mutex, something is terribly wrong");
711
                return;
712
            },
713
            php_ort_status_tensor_notfound_ce, 
714
            "Could not find the Tensor named \"%s\"",
715
                ZSTR_VAL(name));
716
#else
717
        php_ort_status_flow(
495✔
718
            (!shape || !data),
719
            {
720
                return;
721
            },
722
            php_ort_status_tensor_notfound_ce, 
723
            "Could not find the Tensor named \"%s\"",
724
                ZSTR_VAL(name));
725
#endif
726

727
        ort_tensor_t *tensor = 
2,130✔
728
            pecalloc(1, sizeof(ort_tensor_t), 1);
1,645✔
729

730
        tensor->refcount = 1;
1,645✔
731

732
        if (!php_ort_tensor_construct_persistent(tensor, name, shape, data, type)) {
1,645✔
733
#ifdef ZTS
734
            php_ort_status_flow(
120✔
735
                tsrm_mutex_unlock(php_ort_tensor_mutex) != SUCCESS,
736
                return,
737
                php_ort_status_safetyerror_ce,
738
                "it was not possible to release the tensor mutex, something is terribly wrong");
739
#endif
740
            return;
50✔
741
        }
742

743
        ort->object = zend_hash_add_ptr(
1,475✔
744
            &php_ort_tensors,
745
            tensor->name,
435✔
746
            tensor);
435✔
747

748
        ort->object->refcount++;
1,475✔
749
    } else {
485✔
750
        php_ort_atomic_addref(&ort->object->refcount);
34✔
751
    }
752

753
#ifdef ZTS
754
    php_ort_status_flow(
1,064✔
755
        tsrm_mutex_unlock(php_ort_tensor_mutex) != SUCCESS,
756
        return,
757
        php_ort_status_safetyerror_ce,
758
        "it was not possible to release the tensor mutex, something is terribly wrong");
759
#endif
760
}
505✔
761

762
ZEND_BEGIN_ARG_INFO_EX(php_ort_tensor_transient_construct_arginfo, 0, 0, 2)
763
    ZEND_ARG_TYPE_INFO(0, shape, IS_ARRAY, 0)
764
    ZEND_ARG_TYPE_MASK(0, data, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL)
765
    ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
766
ZEND_END_ARG_INFO()
767

768
PHP_METHOD(ONNX_Tensor_Transient, __construct)
37,759✔
769
{
770
    php_ort_tensor_t *ort = php_ort_tensor_fetch(Z_OBJ(EX(This)));
37,759✔
771

772
    zval        *shape = NULL;
37,759✔
773
    zval        *data  = NULL;
37,759✔
774
    zend_long    type  = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64;
37,759✔
775

776
    ZEND_PARSE_PARAMETERS_START(2, 3);
37,759✔
777
        Z_PARAM_ARRAY(shape)
42,189✔
778
        Z_PARAM_ZVAL(data)
37,759✔
779
        Z_PARAM_OPTIONAL
37,759✔
780
        Z_PARAM_LONG(type)
42,185✔
781
    ZEND_PARSE_PARAMETERS_END();
15,505✔
782

783
    ort_tensor_t *tensor = ecalloc(1, sizeof(ort_tensor_t));
37,759✔
784
    tensor->refcount = 1;
37,759✔
785

786
    if (!php_ort_tensor_construct_transient(tensor, shape, data, type)) {
37,759✔
787
        efree(tensor);
153✔
788
        return;
153✔
789
    }
790

791
    ort->object = tensor;
37,606✔
792
}
11,075✔
793

794
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ort_tensor_isPersistent_arginfo, 0, 0, _IS_BOOL, 0)
795
ZEND_END_ARG_INFO()
796

797
PHP_METHOD(ONNX_Tensor, isPersistent)
17✔
798
{
799
    php_ort_tensor_t* ort =
22✔
800
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
17✔
801

802
    ZEND_PARSE_PARAMETERS_NONE();
17✔
803

804
    RETURN_BOOL(ort->object->owner == PHP_ORT_OWN_HEAP);
17✔
805
}
5✔
806

807
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ort_tensor_getName_arginfo, 0, 0, IS_STRING, 1)
808
ZEND_END_ARG_INFO()
809

810
PHP_METHOD(ONNX_Tensor, getName)
51✔
811
{
812
    php_ort_tensor_t* ort =
66✔
813
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
51✔
814
    
815
    ZEND_PARSE_PARAMETERS_NONE();
51✔
816

817
    if (!ort->object->name) {
51✔
818
        return;
819
    }
820

821
    RETURN_STR_COPY(ort->object->name);
51✔
822
}
15✔
823

824
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ort_tensor_getType_arginfo, 0, 0, IS_LONG, 0)
825
ZEND_END_ARG_INFO()
826

827
PHP_METHOD(ONNX_Tensor, getType)
1,037✔
828
{
829
    php_ort_tensor_t* ort =
1,342✔
830
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
1,037✔
831

832
    ZEND_PARSE_PARAMETERS_NONE();
1,037✔
833

834
    RETURN_LONG(ort->object->type);
1,037✔
835
}
305✔
836

837
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ort_tensor_getTypeName_arginfo, 0, 0, IS_STRING, 0)
838
ZEND_END_ARG_INFO()
839

840
PHP_METHOD(ONNX_Tensor, getTypeName)
21,560✔
841
{
842
    php_ort_tensor_t* ort =
27,880✔
843
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
21,560✔
844

845
    ZEND_PARSE_PARAMETERS_NONE();
21,560✔
846

847
    /* @todo(krakjoe) this performs terribly ... */
848
    RETURN_STRING(php_ort_type_name(ort->object->type));
36,604✔
849
}
6,320✔
850

851
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ort_tensor_getShape_arginfo, 0, 0, IS_ARRAY, 0)
852
ZEND_END_ARG_INFO()
853

854
PHP_METHOD(ONNX_Tensor, getShape)
22,291✔
855
{
856
    php_ort_tensor_t* ort =
28,826✔
857
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
22,291✔
858

859
    ZEND_PARSE_PARAMETERS_NONE();
22,291✔
860

861
    array_init(return_value);
22,291✔
862
    for (int64_t dimension = 0; dimension < ort->object->dimensions; dimension++) {
46,197✔
863
        add_next_index_long(
23,906✔
864
            return_value, ort->object->shape[dimension]);
23,906✔
865
    }
7,010✔
866
}
6,535✔
867

868
static zend_always_inline ort_tensor_t* php_ort_tensor_transpose(ort_tensor_t* input, zval *axis, zval* return_value) {
60✔
869
    int64_t ndim = input->dimensions;
68✔
870
    int64_t* perm = NULL;
68✔
871
    int64_t* inv_perm = NULL;
68✔
872
    int64_t* out_shape = NULL;
68✔
873
    int64_t* in_strides = NULL;
68✔
874
    int64_t* out_strides = NULL;
68✔
875
    size_t type_size = php_ort_tensor_sizeof(input);
116✔
876
    size_t numel = input->elements;
68✔
877
    ort_tensor_t* result = NULL;
68✔
878

879
    // Argument validation and axes parsing
880
    if (ndim == 0) {
68✔
881
        // Scalar: transpose is a no-op, just return a copy
882
        result = pecalloc(1, sizeof(ort_tensor_t), 0);
×
883
        result->refcount = 1;
×
884
        result->owner = PHP_ORT_OWN_ZEND;
×
885
        result->type = input->type;
×
886
        result->dimensions = 0;
×
887
        result->elements = 1;
×
888
        result->shape = NULL;
×
889
        result->data = ort_alloc(type_size, 1);
×
890
        ort_memcpy(result->data, input->data, type_size);
×
891
        goto __php_ort_tensor_transpose_done;
×
892
    }
893

894
    perm = ecalloc(ndim, sizeof(int64_t));
68✔
895
    out_shape = ecalloc(ndim, sizeof(int64_t));
68✔
896
    in_strides = ecalloc(ndim, sizeof(int64_t));
68✔
897
    out_strides = ecalloc(ndim, sizeof(int64_t));
68✔
898
    inv_perm = ecalloc(ndim, sizeof(int64_t));
68✔
899

900
    // Parse axes argument
901
    if (axis) {
68✔
902
        php_ort_status_flow(
51✔
903
            (zend_hash_num_elements(Z_ARRVAL_P(axis)) != ndim),
904
            {
905
                goto __php_ort_tensor_transpose_failed;
906
            },
907
            php_ort_status_tensor_invalidshape_ce,
908
            "axes array must be an array of length %zd", ndim);
909

910
        // Fill perm from user axes, handle negatives, check for duplicates
911
        zend_bool* seen = ecalloc(ndim, sizeof(zend_bool));
51✔
912
        int64_t i = 0;
51✔
913
        zval* zv;
51✔
914
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(axis), zv) {
153✔
915
            php_ort_status_flow(
102✔
916
                (Z_TYPE_P(zv) != IS_LONG),
917
                {
918
                    efree(seen);
919
                    goto __php_ort_tensor_transpose_failed;
920
                },
921
                php_ort_status_tensor_invalidshape_ce,
922
                "axes array must contain only integers");
923
            
924
            int64_t ax = Z_LVAL_P(zv);
102✔
925
            if (ax < 0)
102✔
926
                ax += ndim;
34✔
927
            
928
            php_ort_status_flow(
102✔
929
                (ax < 0 || ax >= ndim),
930
                {
931
                    efree(seen);
932
                    goto __php_ort_tensor_transpose_failed;
933
                },
934
                php_ort_status_tensor_invalidshape_ce,
935
                "axis value %zd out of range [0, %zd)", ax, ndim);
936

937
            php_ort_status_flow(
102✔
938
                (seen[ax]),
939
                {
940
                    efree(seen);
941
                    goto __php_ort_tensor_transpose_failed;
942
                },
943
                php_ort_status_tensor_invalidshape_ce,
944
                "duplicate axis value %zd in axes", ax);
945

946
            seen[ax] = 1;
102✔
947
            perm[i++] = ax;
102✔
948
        } ZEND_HASH_FOREACH_END();
45✔
949
        efree(seen);
51✔
950
    } else {
15✔
951
        // Default: reverse axes
952
        for (int64_t i = 0; i < ndim; i++) {
51✔
953
            perm[i] = ndim - 1 - i;
34✔
954
        }
10✔
955
    }
956

957
    // Compute output shape and strides
958
    for (int64_t i = 0; i < ndim; i++) {
204✔
959
        out_shape[i] = input->shape[perm[i]];
136✔
960
    }
40✔
961
    // Input strides
962
    in_strides[ndim-1] = 1;
68✔
963
    for (int64_t i = ndim-2; i >= 0; i--) {
136✔
964
        in_strides[i] = in_strides[i+1] * input->shape[i+1];
68✔
965
    }
20✔
966
    // Output strides
967
    out_strides[ndim-1] = 1;
68✔
968
    for (int64_t i = ndim-2; i >= 0; i--) {
136✔
969
        out_strides[i] = out_strides[i+1] * out_shape[i+1];
68✔
970
    }
20✔
971
    // Inverse permutation: inv_perm[perm[i]] = i
972
    for (int64_t i = 0; i < ndim; i++) {
204✔
973
        inv_perm[perm[i]] = i;
136✔
974
    }
40✔
975

976
    // Allocate result tensor
977
    result = pecalloc(1, sizeof(ort_tensor_t), 0);
68✔
978
    result->refcount = 1;
68✔
979
    result->owner = PHP_ORT_OWN_ZEND;
68✔
980
    result->type = input->type;
68✔
981
    result->dimensions = ndim;
68✔
982
    result->elements = numel;
68✔
983
    result->shape = pecalloc(ndim, sizeof(int64_t), 0);
68✔
984
    memcpy(result->shape,
68✔
985
        out_shape, ndim * sizeof(int64_t));
986
    result->data = ort_alloc(type_size, numel);
68✔
987

988
    // Main permutation loop
989
    int64_t* in_coords =
68✔
990
        ecalloc(ndim, sizeof(int64_t));
68✔
991
    int64_t* out_coords =
68✔
992
        ecalloc(ndim, sizeof(int64_t));
68✔
993
    for (size_t idx = 0; idx < numel; idx++) {
680✔
994
        // Compute input coordinates from flat idx
995
        size_t rem = idx;
180✔
996
        for (int64_t i = 0; i < ndim; i++) {
1,836✔
997
            in_coords[i] = rem / in_strides[i];
1,224✔
998
            rem = rem % in_strides[i];
1,224✔
999
        }
360✔
1000
        // Permute coordinates
1001
        for (int64_t i = 0; i < ndim; i++) {
1,836✔
1002
            out_coords[i] = in_coords[perm[i]];
1,224✔
1003
        }
360✔
1004
        // Compute output flat index
1005
        size_t out_idx = 0;
180✔
1006
        for (int64_t i = 0; i < ndim; i++) {
1,836✔
1007
            out_idx += out_coords[i] * out_strides[i];
1,224✔
1008
        }
360✔
1009
        // Copy element
1010
        ort_memcpy(
612✔
1011
            (char*)result->data + out_idx * type_size,
612✔
1012
            (char*)input->data + idx * type_size,
612✔
1013
            type_size);
180✔
1014
    }
180✔
1015
    efree(in_coords);
68✔
1016
    efree(out_coords);
68✔
1017

1018
__php_ort_tensor_transpose_done:
48✔
1019
    object_init_ex(return_value,
88✔
1020
        php_ort_tensor_transient_ce);
20✔
1021
    php_ort_tensor_t* rv =
68✔
1022
        php_ort_tensor_fetch(
68✔
1023
            Z_OBJ_P(return_value));
20✔
1024
    rv->object = result;
68✔
1025

1026
    if (perm)
68✔
1027
        efree(perm);
68✔
1028
    if (out_shape)
68✔
1029
        efree(out_shape);
68✔
1030
    if (in_strides)
68✔
1031
        efree(in_strides);
68✔
1032
    if (out_strides)
68✔
1033
        efree(out_strides);
68✔
1034
    if (inv_perm)
68✔
1035
        efree(inv_perm);
68✔
1036
    return result;
20✔
1037

1038
__php_ort_tensor_transpose_failed:
1039
    if (perm)
×
1040
        efree(perm);
×
1041
    if (out_shape)
×
1042
        efree(out_shape);
×
1043
    if (in_strides)
×
1044
        efree(in_strides);
×
1045
    if (out_strides)
×
1046
        efree(out_strides);
×
1047
    if (inv_perm)
×
1048
        efree(inv_perm);
×
1049
    return NULL;
1050
}
20✔
1051

1052
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(php_ort_tensor_transpose_arginfo, 0, 0, ORT\\Tensor, 0)
1053
    ZEND_ARG_TYPE_INFO(0, axis, IS_ARRAY, 1)
1054
ZEND_END_ARG_INFO()
1055

1056
PHP_METHOD(ONNX_Tensor, transpose)
68✔
1057
{
1058
    php_ort_tensor_t* ort =
88✔
1059
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
68✔
1060
    zval* axis = NULL;
68✔
1061

1062
    ZEND_PARSE_PARAMETERS_START(0, 1);
68✔
1063
        Z_PARAM_OPTIONAL
68✔
1064
        Z_PARAM_ZVAL(axis)
68✔
1065
    ZEND_PARSE_PARAMETERS_END();
58✔
1066

1067
    php_ort_tensor_transpose(ort->object, axis, return_value);
68✔
1068
}
20✔
1069

1070
static zend_always_inline ort_tensor_t* php_ort_tensor_slice(ort_tensor_t* input, zval* start, zval* end, zval* axis, zval* return_value) {
90✔
1071
    // Handle axis parameter - if provided, validate it
1072
    zend_bool has_axis = (axis != NULL);
102✔
1073
    HashTable *axis_ht = has_axis ? Z_ARRVAL_P(axis) : NULL;
174✔
1074

1075
    if (has_axis) {
102✔
1076
        // When axis is provided, start and end should match axis array length
1077
        php_ort_status_flow(
17✔
1078
            (zend_hash_num_elements(Z_ARRVAL_P(start)) != zend_hash_num_elements(axis_ht) ||
1079
             zend_hash_num_elements(Z_ARRVAL_P(end))   != zend_hash_num_elements(axis_ht)), 
1080
            return NULL,
1081
            php_ort_status_tensor_invalidshape_ce,
1082
            "when axis is provided, start and end arrays must have same length as axis array (%zd)",
1083
            zend_hash_num_elements(axis_ht));
1084
    } else {
5✔
1085
        // When no axis provided, start and end must match tensor dimensions
1086
        php_ort_status_flow(
85✔
1087
            (zend_hash_num_elements(Z_ARRVAL_P(start)) != input->dimensions ||
1088
             zend_hash_num_elements(Z_ARRVAL_P(end))   != input->dimensions), 
1089
            return NULL,
1090
            php_ort_status_tensor_invalidshape_ce,
1091
            "start and end arrays must have same length as tensor dimensions (%zd)",
1092
            input->dimensions);
1093
    }
1094

1095
    object_init_ex(return_value, php_ort_tensor_transient_ce);
85✔
1096

1097
    php_ort_tensor_t* ort = php_ort_tensor_fetch(Z_OBJ_P(return_value));
85✔
1098
    
1099
    // Allocate new tensor structure
1100
    ort->object = pecalloc(1, sizeof(ort_tensor_t), 0);
85✔
1101
    
1102
    // Set up parent relationship
1103
    ort->object->parent = input;
85✔
1104
    php_ort_atomic_addref(
85✔
1105
        &ort->object->parent->refcount);
25✔
1106

1107
    ort->object->refcount = 1;
85✔
1108
    ort->object->owner = PHP_ORT_OWN_ZEND;
85✔
1109
    ort->object->type = input->type;
85✔
1110

1111
    int64_t *starting = pecalloc(input->dimensions, sizeof(int64_t), 0);
85✔
1112
    int64_t *ending = pecalloc(input->dimensions, sizeof(int64_t), 0);
85✔
1113
    int64_t *slicing = pecalloc(input->dimensions, sizeof(int64_t), 0);
85✔
1114

1115
    // Initialize with full range for all dimensions
1116
    for (int64_t idim = 0; idim < input->dimensions; idim++) {
255✔
1117
        starting[idim] = 0;
170✔
1118
        ending[idim]   = input->shape[idim];
170✔
1119
        slicing[idim]  = input->shape[idim];
170✔
1120
    }
50✔
1121

1122
    // Process based on whether axis is provided
1123
    if (has_axis) {
85✔
1124
        // Process each axis specification
1125
        zval *zaxis, *zstart, *zend;
17✔
1126
        zend_ulong iaxis = 0;
17✔
1127
        
1128
        ZEND_HASH_FOREACH_VAL(axis_ht, zaxis) {
34✔
1129
            php_ort_status_flow(
17✔
1130
                (Z_TYPE_P(zaxis) != IS_LONG),
1131
                {
1132
                    pefree(starting, 0);
1133
                    pefree(ending, 0);
1134
                    pefree(slicing, 0);
1135
                    return NULL;
1136
                },
1137
                php_ort_status_tensor_invalidshape_ce,
1138
                "axis array must contain integers");
1139
            
1140
            int64_t idim = Z_LVAL_P(zaxis);
17✔
1141

1142
            php_ort_status_flow(
17✔
1143
                (idim < 0 || idim >= input->dimensions),
1144
                {
1145
                    pefree(starting, 0);
1146
                    pefree(ending, 0);
1147
                    pefree(slicing, 0);
1148

1149
                    return NULL;
1150
                },
1151
                php_ort_status_tensor_invalidshape_ce,
1152
                "axis value %zd out of range [0, %zd)", idim, input->dimensions);
1153
            
1154
            // Get corresponding start and end values using the same index
1155
            zstart = zend_hash_index_find(Z_ARRVAL_P(start), iaxis);
17✔
1156
            zend   = zend_hash_index_find(Z_ARRVAL_P(end),   iaxis);
17✔
1157
            
1158
            php_ort_status_flow(
17✔
1159
                (!zstart || !zend || 
1160
                  Z_TYPE_P(zstart) != IS_LONG || 
1161
                  Z_TYPE_P(zend)   != IS_LONG),
1162
                {
1163
                    pefree(starting, 0);
1164
                    pefree(ending, 0);
1165
                    pefree(slicing, 0);
1166

1167
                    return NULL;
1168
                },
1169
                php_ort_status_tensor_invalidshape_ce,
1170
                "start and end values must be integers");
1171

1172
            int64_t istart = Z_LVAL_P(zstart);
17✔
1173
            int64_t iend = Z_LVAL_P(zend);
17✔
1174
            
1175
            // Handle negative indices
1176
            if (istart < 0) istart += input->shape[idim];
17✔
1177
            if (iend < 0)   iend += input->shape[idim];
17✔
1178

1179
            // Validate bounds
1180
            php_ort_status_flow(
17✔
1181
                (istart < 0 || istart >= input->shape[idim] ||
1182
                 iend < 0   || iend    > input->shape[idim] ||
1183
                 istart >= iend),
1184
                {
1185
                    pefree(starting, 0);
1186
                    pefree(ending, 0);
1187
                    pefree(slicing, 0);
1188
                    return NULL;
1189
                },
1190
                php_ort_status_tensor_invalidshape_ce,
1191
                "invalid slice bounds for axis %zd: start=%zd, end=%zd, shape=%zd",
1192
                idim, istart, iend, input->shape[idim]);
1193

1194
            starting[idim] = istart;
17✔
1195
            ending[idim]   = iend;
17✔
1196
            slicing[idim]  = iend - istart;
17✔
1197

1198
            iaxis++;
17✔
1199
        } ZEND_HASH_FOREACH_END();
10✔
1200
    } else {
5✔
1201
        // No axis specified, use start and end arrays directly for all dimensions
1202
        for (int64_t idim = 0; idim < input->dimensions; idim++) {
146✔
1203
            zval *zstart = zend_hash_index_find(Z_ARRVAL_P(start), idim);
102✔
1204
            zval *zend   = zend_hash_index_find(Z_ARRVAL_P(end),   idim);
102✔
1205

1206
            php_ort_status_flow(
102✔
1207
                (!zstart || !zend ||
1208
                 Z_TYPE_P(zstart) != IS_LONG ||
1209
                 Z_TYPE_P(zend)   != IS_LONG),
1210
                {
1211
                    pefree(starting, 0);
1212
                    pefree(ending, 0);
1213
                    pefree(slicing, 0);
1214
                    return NULL;
1215
                },
1216
                php_ort_status_tensor_invalidshape_ce,
1217
                "start and end values must be integers");
1218

1219
            int64_t istart = Z_LVAL_P(zstart);
102✔
1220
            int64_t iend = Z_LVAL_P(zend);
102✔
1221

1222
            // Handle negative indices
1223
            if (istart < 0) istart += input->shape[idim];
102✔
1224
            if (iend < 0)   iend   += input->shape[idim];
102✔
1225

1226
            php_ort_status_flow(
102✔
1227
                (istart < 0 || istart >= input->shape[idim] ||
1228
                 iend < 0   || iend   >  input->shape[idim] ||
1229
                 istart     >= iend),
1230
                {
1231
                    pefree(starting, 0);
1232
                    pefree(ending, 0);
1233
                    pefree(slicing, 0);
1234

1235
                    return NULL;
1236
                },
1237
                php_ort_status_tensor_invalidshape_ce,
1238
                "invalid slice bounds for dimension %zd: start=%zd, end=%zd, shape=%zd",
1239
                idim, istart, iend, input->shape[idim]);
1240

1241
            starting[idim] = istart;
68✔
1242
            ending[idim]   = iend;
68✔
1243
            slicing[idim]  = iend - istart;
68✔
1244
        }
30✔
1245
    }
1246

1247
    // Set up slice tensor metadata
1248
    ort->object->dimensions = input->dimensions;
51✔
1249
    ort->object->shape      = slicing; // Transfer ownership
51✔
1250

1251
    // Calculate number of elements in slice
1252
    ort->object->elements = 1;
51✔
1253
    for (int64_t idim = 0; idim < ort->object->dimensions; idim++) {
153✔
1254
        ort->object->elements *= ort->object->shape[idim];
102✔
1255
    }
30✔
1256

1257
    // Set data pointer to offset into parent's data (readonly view)
1258
    ort->object->data = (char*)input->data + 
66✔
1259
        (php_ort_tensor_indexof(input, starting) * 
66✔
1260
            php_ort_tensor_sizeof(input));
51✔
1261

1262
    pefree(starting, 0);
51✔
1263
    pefree(ending, 0);
51✔
1264

1265
    return ort->object;
51✔
1266
}
30✔
1267

1268
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(php_ort_tensor_getSlice_arginfo, 0, 2, ORT\\Tensor, 0)
1269
    ZEND_ARG_TYPE_INFO(0, start, IS_ARRAY, 0)
1270
    ZEND_ARG_TYPE_INFO(0, end,   IS_ARRAY, 0)
1271
    ZEND_ARG_TYPE_INFO(0, axis,  IS_ARRAY, 0)
1272
ZEND_END_ARG_INFO()
1273

1274
PHP_METHOD(ONNX_Tensor, getSlice)
102✔
1275
{
1276
    php_ort_tensor_t* ort =
132✔
1277
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
102✔
1278
    zval *start, *end, *axis = NULL;
102✔
1279

1280
    ZEND_PARSE_PARAMETERS_START(2, 3)
102✔
1281
        Z_PARAM_ARRAY(start)
114✔
1282
        Z_PARAM_ARRAY(end)
114✔
1283
        Z_PARAM_OPTIONAL
102✔
1284
        Z_PARAM_ARRAY(axis)
104✔
1285
    ZEND_PARSE_PARAMETERS_END();
42✔
1286

1287
    php_ort_tensor_slice(ort->object, start, end, axis, return_value);
102✔
1288
}
30✔
1289

1290
static inline zend_bool php_ort_tensor_data(ort_tensor_t *tensor, size_t *offset, size_t size, zval *node, size_t depth) {
237,547✔
1291
    // Special case for scalar tensors (0 dimensions)
1292
    if (tensor->dimensions == 0) {
237,547✔
1293
        // This should not be called for scalar tensors
1294
        // The getData method directly handles scalar case
1295
        php_ort_status_flow(
×
1296
            !SUCCESS,
1297
            return 0,
1298
            php_ort_status_tensor_invalidshape_ce,
1299
            "php_ort_tensor_data should not be called for scalar tensors");
1300
    }
1301

1302
    // Validate depth bounds
1303
    php_ort_status_flow(
237,547✔
1304
        (depth >= tensor->dimensions),
1305
        return 0,
1306
        php_ort_status_tensor_invalidshape_ce,
1307
        "depth %zd exceeds tensor dimensions %zd", depth, tensor->dimensions);
1308

1309
    // Validate shape at current depth
1310
    php_ort_status_flow(
237,547✔
1311
        (tensor->shape[depth] <= 0),
1312
        return 0,
1313
        php_ort_status_tensor_invalidshape_ce,
1314
        "invalid shape at dimension %zd: %zd", depth, tensor->shape[depth]);
1315

1316
    // Calculate remaining elements from current offset
1317
    size_t remaining_elements = (*offset < tensor->elements) ? (tensor->elements - *offset) : 0;
237,547✔
1318
    
1319
    // Calculate how many elements we can actually extract at this depth
1320
    int64_t elements_to_extract = tensor->shape[depth];
237,547✔
1321
    if (depth == tensor->dimensions - 1) {
237,547✔
1322
        // At leaf level, limit to remaining elements
1323
        elements_to_extract = (remaining_elements < (size_t)tensor->shape[depth]) ? 
234,376✔
1324
                             (int64_t)remaining_elements : tensor->shape[depth];
234,376✔
1325
    }
68,900✔
1326

1327
    // Initialize array with actual size we'll extract
1328
    array_init_size(node, elements_to_extract);
237,547✔
1329

1330
    if (depth == tensor->dimensions - 1) {
237,547✔
1331
        // Leaf level - extract scalar values
1332
        for (int64_t i = 0; i < elements_to_extract; i++) {
176,528,936✔
1333
            zval val;
176,294,560✔
1334
            
1335
            // Validate offset bounds before accessing data
1336
            php_ort_status_flow(
176,294,560✔
1337
                (*offset >= tensor->elements),
1338
                return 0,
1339
                php_ort_status_tensor_invaliddata_ce,
1340
                "data offset %zd exceeds tensor element count %zd", *offset, tensor->elements);
1341

1342
            void *source = (char *)tensor->data + ((*offset) * size);
176,294,560✔
1343

1344
            switch (tensor->type) {
176,294,560✔
1345
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
1,079,760✔
1346
                    ZVAL_DOUBLE(&val, ort_math_float64_from_float16(*(float16 *)source));
1,529,660✔
1347
                break;
1,529,660✔
1348

1349
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32:
13,968,632✔
1350
                    ZVAL_DOUBLE(&val, *(float32 *)source);
19,695,032✔
1351
                    break;
19,695,032✔
1352

1353
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64:
14,287,664✔
1354
                    ZVAL_DOUBLE(&val, *(float64 *)source);
20,152,109✔
1355
                    break;
20,152,109✔
1356

1357
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
13,648,284✔
1358
                    ZVAL_LONG(&val, *(int8_t *)source);
19,335,069✔
1359
                    break;
19,335,069✔
1360

1361
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
13,593,208✔
1362
                    ZVAL_LONG(&val, *(int16_t *)source);
19,257,043✔
1363
                    break;
19,257,043✔
1364

1365
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
13,593,700✔
1366
                    ZVAL_LONG(&val, *(int32_t *)source);
19,257,740✔
1367
                    break;
19,257,740✔
1368

1369
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
13,595,040✔
1370
                    ZVAL_LONG(&val, *(int64_t *)source);
19,259,640✔
1371
                    break;
19,259,640✔
1372

1373
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
13,643,928✔
1374
                    ZVAL_LONG(&val, *(uint8_t *)source);
19,323,778✔
1375
                    break;
19,323,778✔
1376

1377
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
13,582,540✔
1378
                    ZVAL_LONG(&val, *(uint16_t *)source);
19,241,930✔
1379
                    break;
19,241,930✔
1380

1381
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
13,582,540✔
1382
                    ZVAL_LONG(&val, *(uint32_t *)source);
19,241,930✔
1383
                    break;
19,241,930✔
1384

1385
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64:
1386
                    // UINT64 is not supported - this should not be reached
1387
                    php_ort_status_flow(
×
1388
                        !SUCCESS,
1389
                        {
1390
                            return 0;
1391
                        },
1392
                        php_ort_status_tensor_invalidtype_ce,
1393
                        "UINT64 tensor type is not supported (values exceed PHP integer range)");
1394

1395
                case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
1396
                    ZVAL_BOOL(&val, *(uint8_t *)source ? 1 : 0);
629✔
1397
                    break;
629✔
1398

1399
                default:
1400
                    php_ort_status_flow(
×
1401
                        !SUCCESS,
1402
                        {
1403
                            return 0;
1404
                        },
1405
                        php_ort_status_tensor_invaliddata_ce,
1406
                        "unsupported tensor data type: %d", tensor->type);
1407
            }
1408

1409
            php_ort_status_flow(
176,294,560✔
1410
                (zend_hash_index_update(Z_ARRVAL_P(node), i, &val) == NULL),
1411
                return 0,
1412
                php_ort_status_tensor_invaliddata_ce,
1413
                "failed to update array at index %zd", i);
1414

1415
            (*offset)++;
176,294,560✔
1416
        }
51,718,820✔
1417
    } else {
68,900✔
1418
        // Recursive case - process sub-dimensions
1419
        // For non-leaf levels, we need to check if we have enough remaining elements
1420
        // to fill the expected sub-structures
1421
        for (int64_t i = 0; i < elements_to_extract; i++) {
216,887✔
1422
            // Check if we still have elements remaining before processing
1423
            if (*offset >= tensor->elements) {
213,740✔
1424
                break; // No more elements available
10✔
1425
            }
1426
            
1427
            zval child;
213,706✔
1428
            
1429
            php_ort_status_flow(
213,706✔
1430
                (!php_ort_tensor_data(tensor, offset, size, &child, depth + 1)),
1431
                return 0,
1432
                php_ort_status_tensor_invaliddata_ce,
1433
                "failed to extract data at dimension %zd, index %zd", depth, i);
1434

1435
            php_ort_status_flow(
213,706✔
1436
                (zend_hash_index_update(Z_ARRVAL_P(node), i, &child) == NULL),
1437
                return 0,
1438
                php_ort_status_tensor_invaliddata_ce,
1439
                "failed to update array at dimension %zd, index %zd", depth, i);
1440
        }
62,860✔
1441
    }
1442

1443
    return 1;
69,835✔
1444
}
69,835✔
1445

1446
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ort_tensor_getData_arginfo, 0, 0, IS_ARRAY, 0)
1447
    ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
1448
    ZEND_ARG_TYPE_INFO(0, depth,  IS_LONG, 0)
1449
ZEND_END_ARG_INFO()
1450

1451
PHP_METHOD(ONNX_Tensor, getData)
25,167✔
1452
{
1453
    php_ort_tensor_t* ort =
32,532✔
1454
        php_ort_tensor_fetch(Z_OBJ(EX(This)));
25,167✔
1455
    zend_long offset = 0, depth = 0;
25,167✔
1456

1457
    ZEND_PARSE_PARAMETERS_START(0, 2)
25,167✔
1458
        Z_PARAM_OPTIONAL
25,167✔
1459
        Z_PARAM_LONG(offset)
25,211✔
1460
        Z_PARAM_LONG(depth)
402✔
1461
    ZEND_PARSE_PARAMETERS_END();
10,309✔
1462

1463
    php_ort_status_flow(
25,167✔
1464
        (offset < 0 || offset > ort->object->elements),
1465
        return,
1466
        php_ort_status_tensor_invaliddata_ce,
1467
        "offset %zd out of range [0, %zd]",
1468
        offset, ort->object->elements);
1469

1470
    // Special case for scalar tensors (0 dimensions)
1471
    if (ort->object->dimensions == 0) {
25,116✔
1472
        // For scalar tensors, check if depth parameter was provided
1473
        php_ort_status_flow(
1,190✔
1474
            (ZEND_NUM_ARGS() > 1 && depth != 0),
1475
            return,
1476
            php_ort_status_tensor_invalidshape_ce,
1477
            "depth parameter cannot be used with scalar tensors");
1478

1479
        // Handle offset parameter - for scalar tensors, offset 0 returns the value, any other offset returns empty array
1480
        if (offset > 0) {
1,173✔
1481
            array_init(return_value);
17✔
1482
            return;
17✔
1483
        }
1484
        
1485
        // For scalar tensors, return a single-element array with the scalar value
1486
        array_init_size(return_value, 1);
1,156✔
1487
        zval val;
1,156✔
1488
        
1489
        void *source = ort->object->data;
1,156✔
1490
        
1491
        switch (ort->object->type) {
1,156✔
1492
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
48✔
1493
                ZVAL_DOUBLE(&val, ort_math_float64_from_float16(*(float16 *)source));
68✔
1494
                break;
68✔
1495

1496
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32:
156✔
1497
                ZVAL_DOUBLE(&val, *(float32 *)source);
221✔
1498
                break;
221✔
1499

1500
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64:
156✔
1501
                ZVAL_DOUBLE(&val, *(float64 *)source);
221✔
1502
                break;
221✔
1503

1504
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
24✔
1505
                ZVAL_LONG(&val, *(int8_t *)source);
34✔
1506
                break;
34✔
1507

1508
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
24✔
1509
                ZVAL_LONG(&val, *(int16_t *)source);
34✔
1510
                break;
34✔
1511

1512
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
24✔
1513
                ZVAL_LONG(&val, *(int32_t *)source);
34✔
1514
                break;
34✔
1515

1516
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
264✔
1517
                ZVAL_LONG(&val, *(int64_t *)source);
374✔
1518
                break;
374✔
1519

1520
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
24✔
1521
                ZVAL_LONG(&val, *(uint8_t *)source);
34✔
1522
                break;
34✔
1523

1524
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
24✔
1525
                ZVAL_LONG(&val, *(uint16_t *)source);
34✔
1526
                break;
34✔
1527

1528
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
24✔
1529
                ZVAL_LONG(&val, *(uint32_t *)source);
34✔
1530
                break;
34✔
1531

1532
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64:
1533
                // UINT64 is not supported - this should not be reached
1534
                php_ort_status_flow(
×
1535
                    !SUCCESS,
1536
                    return,
1537
                    php_ort_status_tensor_invalidtype_ce,
1538
                    "UINT64 tensor type is not supported (values exceed PHP integer range)");
1539

1540
            case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
1541
                ZVAL_BOOL(&val, *(uint8_t *)source ? 1 : 0);
68✔
1542
                break;
68✔
1543

1544
            default:
1545
                php_ort_status_flow(
×
1546
                    !SUCCESS,
1547
                    return,
1548
                    php_ort_status_tensor_invaliddata_ce,
1549
                    "unsupported tensor data type: %d", ort->object->type);
1550
        }
1551
        
1552
        add_index_zval(return_value, 0, &val);
1,156✔
1553
        return;
1,156✔
1554
    }
340✔
1555

1556
    // Normal case for non-scalar tensors
1557
    php_ort_status_flow(
23,926✔
1558
        (depth < 0 || depth >= ort->object->dimensions),
1559
        return,
1560
        php_ort_status_tensor_invalidshape_ce,
1561
        "depth %zd out of range [0, %zd)",
1562
        depth, ort->object->dimensions);
1563

1564
    size_t offset_size = (size_t)offset;
23,841✔
1565
    php_ort_status_flow(
40,707✔
1566
        (!php_ort_tensor_data(
1567
            ort->object, 
1568
            &offset_size,
1569
            php_ort_tensor_sizeof(ort->object), 
1570
            return_value, 
1571
            (size_t)depth)),
1572
        return,
1573
        php_ort_status_tensor_invaliddata_ce,
1574
        "failed to extract tensor data starting at offset %zd, depth %zd", offset_size, depth);
1575
}
7,365✔
1576

1577
// Robust recursive shape inference with raggedness and type checks
1578
static zend_bool php_ort_infer_shape(zval *data, size_t *shape, size_t max, size_t *dimensions) {
595✔
1579
    size_t dimension = 0;
595✔
1580
    zval *level = data;
595✔
1581
    while (Z_TYPE_P(level) == IS_ARRAY) {
884✔
1582
        php_ort_status_flow(
884✔
1583
            (dimension >= max),
1584
            return 0,
1585
            php_ort_status_tensor_invaliddata_ce,
1586
            "shape exceeds maximum allowed dimensions (%zd)", max);
1587

1588
        size_t len = zend_hash_num_elements(Z_ARRVAL_P(level));
884✔
1589

1590
        php_ort_status_flow(
884✔
1591
            len == 0,
1592
            return 0,
1593
            php_ort_status_tensor_invaliddata_ce,
1594
            "empty array encountered at dimension %zd (ragged or empty tensor)",
1595
            dimension);
1596

1597
        shape[dimension] = len;
850✔
1598

1599
        // Check all elements at this level are arrays or all are scalars
1600
        zend_bool found_array = 0, found_scalar = 0;
850✔
1601
        zval *first = NULL;
850✔
1602
        zend_bool first_is_array = 0;
850✔
1603
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(level), first) {
850✔
1604
            if (Z_TYPE_P(first) == IS_ARRAY) {
850✔
1605
                first_is_array = 1;
285✔
1606
            }
95✔
1607
            break;
250✔
1608
        } ZEND_HASH_FOREACH_END();
250✔
1609

1610
        if (first && first_is_array) {
850✔
1611
            found_array = 1;
95✔
1612
        } else {
95✔
1613
            found_scalar = 1;
465✔
1614
        }
1615

1616
        size_t expected_len =
1,700✔
1617
            (first && first_is_array) ?
350✔
1618
                zend_hash_num_elements(Z_ARRVAL_P(first)) : 0;
733✔
1619

1620
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(level), zval *sub) {
155,615✔
1621
            if (Z_TYPE_P(sub) == IS_ARRAY) {
154,799✔
1622
                found_array = 1;
1,377✔
1623
                php_ort_status_flow(
1,377✔
1624
                    (expected_len != 0 && zend_hash_num_elements(Z_ARRVAL_P(sub)) != expected_len),
1625
                    return 0,
1626
                    php_ort_status_tensor_invaliddata_ce,
1627
                    "ragged array: sub-array at dimension %zd has length %zd, expected %zd",
1628
                    dimension+1,
1629
                    zend_hash_num_elements(Z_ARRVAL_P(sub)),
1630
                    expected_len);
1631
            } else {
395✔
1632
                found_scalar = 1;
41,510✔
1633
            }
1634

1635
            php_ort_status_flow(
154,765✔
1636
                (Z_TYPE_P(sub) != IS_ARRAY &&
1637
                Z_TYPE_P(sub) != IS_LONG &&
1638
                Z_TYPE_P(sub) != IS_DOUBLE &&
1639
                !(Z_TYPE_P(sub) == IS_TRUE || Z_TYPE_P(sub) == IS_FALSE)),
1640
                return 0,
1641
                php_ort_status_tensor_invaliddata_ce,
1642
                "unsupported type at dimension %zd: %s",
1643
                dimension+1,
1644
                zend_zval_type_name(sub));
1645

1646
            php_ort_status_flow(
154,765✔
1647
                (found_array && found_scalar),
1648
                return 0,
1649
                php_ort_status_tensor_invaliddata_ce,
1650
                "mixed array/scalar types at dimension %zd (ragged tensor)",
1651
                dimension+1);
1652
        } ZEND_HASH_FOREACH_END();
42,155✔
1653
        if (found_array) {
816✔
1654
            // Go one level deeper
1655
            level = first;
289✔
1656
            dimension++;
289✔
1657
        } else {
85✔
1658
            // All scalars at this level, this is the last dimension
1659
            dimension++;
527✔
1660
            break;
527✔
1661
        }
1662
    }
260✔
1663
    *dimensions = dimension;
527✔
1664
    return 1;
527✔
1665
}
175✔
1666

1667
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(php_ort_tensor_persistent_from_arginfo, 0, 3, ORT\\Tensor, 0)
1668
    ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
1669
    ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0)
1670
    ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
1671
ZEND_END_ARG_INFO()
1672

1673
PHP_METHOD(ONNX_Tensor_Persistent, from)
119✔
1674
{
1675
    zend_string* name;
119✔
1676
    zval *data;
119✔
1677
    zend_long type;
119✔
1678
    size_t shape[32];
119✔
1679
    size_t dimensions = 0;
119✔
1680

1681
    ZEND_PARSE_PARAMETERS_START(3, 3)
119✔
1682
        Z_PARAM_STR(name)
133✔
1683
        Z_PARAM_ARRAY(data)
133✔
1684
        Z_PARAM_LONG(type)
133✔
1685
    ZEND_PARSE_PARAMETERS_END();
49✔
1686

1687
    if (!php_ort_infer_shape(data, shape, 32, &dimensions)) {
119✔
1688
        return;
10✔
1689
    }
1690

1691
    php_ort_status_flow(
85✔
1692
        (dimensions == 0),
1693
        return,
1694
        php_ort_status_tensor_invaliddata_ce,
1695
        "empty tensor data provided (no dimensions inferred)");
1696

1697
    zval param;
85✔
1698
    array_init_size(&param, dimensions);
85✔
1699
    for (size_t i = 0; i < dimensions; i++) {
238✔
1700
        add_next_index_long(&param, shape[i]);
153✔
1701
    }
45✔
1702

1703
    zend_class_entry *scope =
110✔
1704
        zend_get_executed_scope();
85✔
1705
    object_init_ex(return_value, scope);
85✔
1706

1707
    zval params[4];
85✔
1708
    ZVAL_STR(&params[0], name);
85✔
1709
    ZVAL_ARR(&params[1], Z_ARRVAL(param));
85✔
1710
    ZVAL_ARR(&params[2], Z_ARRVAL_P(data));
85✔
1711
    ZVAL_LONG(&params[3], type);
85✔
1712

1713
    zval constructor;
85✔
1714
    ZVAL_STRING(&constructor, "__construct");
85✔
1715
    zval retval;
85✔
1716
    if (SUCCESS == call_user_function(
85✔
1717
            EG(function_table),
1718
            return_value,
1719
            &constructor,
1720
            &retval,
1721
            4,
1722
            params)) {
1723
        zval_ptr_dtor(&retval);
85✔
1724
    } else {
25✔
1725
        zval_ptr_dtor(return_value);
×
1726
    }
1727

1728
    zval_ptr_dtor(&constructor);
85✔
1729
    zval_ptr_dtor(&param);
85✔
1730
}
35✔
1731

1732
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(php_ort_tensor_transient_from_arginfo, 0, 2, ORT\\Tensor, 0)
1733
    ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0)
1734
    ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
1735
ZEND_END_ARG_INFO()
1736

1737
PHP_METHOD(ONNX_Tensor_Transient, from)
476✔
1738
{
1739
    zval *data;
476✔
1740
    zend_long type;
476✔
1741
    size_t shape[32];
476✔
1742
    size_t dimensions = 0;
476✔
1743

1744
    ZEND_PARSE_PARAMETERS_START(2, 2)
476✔
1745
        Z_PARAM_ARRAY(data)
532✔
1746
        Z_PARAM_LONG(type)
532✔
1747
    ZEND_PARSE_PARAMETERS_END();
196✔
1748

1749
    if (!php_ort_infer_shape(data, shape, 32, &dimensions)) {
476✔
1750
        return;
10✔
1751
    }
1752

1753
    php_ort_status_flow(
442✔
1754
        (dimensions == 0),
1755
        return,
1756
        php_ort_status_tensor_invaliddata_ce,
1757
        "empty tensor data provided (no dimensions inferred)");
1758

1759
    zval param;
442✔
1760
    array_init_size(&param, dimensions);
442✔
1761
    for (size_t i = 0; i < dimensions; i++) {
1,105✔
1762
        add_next_index_long(&param, shape[i]);
663✔
1763
    }
195✔
1764

1765
    zend_class_entry *scope =
572✔
1766
        zend_get_executed_scope();
442✔
1767
    object_init_ex(return_value, scope);
442✔
1768

1769
    zval params[3];
442✔
1770
    ZVAL_ARR(&params[0], Z_ARRVAL(param));
442✔
1771
    ZVAL_ARR(&params[1], Z_ARRVAL_P(data));
442✔
1772
    ZVAL_LONG(&params[2], type);
442✔
1773

1774
    zval constructor;
442✔
1775
    ZVAL_STRING(&constructor, "__construct");
442✔
1776
    zval retval;
442✔
1777
    if (SUCCESS == call_user_function(
442✔
1778
            EG(function_table),
1779
            return_value,
1780
            &constructor,
1781
            &retval,
1782
            3,
1783
            params)) {
1784
        zval_ptr_dtor(&retval);
442✔
1785
    } else {
130✔
1786
        zval_ptr_dtor(return_value);
×
1787
    }
1788

1789
    zval_ptr_dtor(&constructor);
442✔
1790
    zval_ptr_dtor(&param);
442✔
1791
}
140✔
1792

1793
zend_function_entry php_ort_tensor_interface_methods[] = {
1794
    PHP_ABSTRACT_ME(ONNX_Tensor, isPersistent, php_ort_tensor_isPersistent_arginfo)
1795
    PHP_ABSTRACT_ME(ONNX_Tensor, getName,      php_ort_tensor_getName_arginfo)
1796
    PHP_ABSTRACT_ME(ONNX_Tensor, getType,      php_ort_tensor_getType_arginfo)
1797
    PHP_ABSTRACT_ME(ONNX_Tensor, getTypeName,  php_ort_tensor_getTypeName_arginfo)
1798
    PHP_ABSTRACT_ME(ONNX_Tensor, getShape,     php_ort_tensor_getShape_arginfo)
1799
    PHP_ABSTRACT_ME(ONNX_Tensor, getSlice,     php_ort_tensor_getSlice_arginfo)
1800
    PHP_ABSTRACT_ME(ONNX_Tensor, getData,      php_ort_tensor_getData_arginfo)
1801

1802
    PHP_ABSTRACT_ME(ONNX_Tensor, transpose,    php_ort_tensor_transpose_arginfo)
1803
    PHP_FE_END
1804
};
1805

1806
zend_function_entry php_ort_tensor_persistent_methods[] = {
1807
    PHP_ME(ONNX_Tensor_Persistent, __construct,
1808
        php_ort_tensor_persistent_construct_arginfo, ZEND_ACC_PUBLIC)
1809
    PHP_ME(ONNX_Tensor, isPersistent, php_ort_tensor_isPersistent_arginfo, ZEND_ACC_PUBLIC)
1810
    PHP_ME(ONNX_Tensor, getName,      php_ort_tensor_getName_arginfo,      ZEND_ACC_PUBLIC)
1811
    PHP_ME(ONNX_Tensor, getType,      php_ort_tensor_getType_arginfo,      ZEND_ACC_PUBLIC)
1812
    PHP_ME(ONNX_Tensor, getTypeName,  php_ort_tensor_getTypeName_arginfo,  ZEND_ACC_PUBLIC)
1813
    PHP_ME(ONNX_Tensor, getShape,     php_ort_tensor_getShape_arginfo,     ZEND_ACC_PUBLIC)
1814
    PHP_ME(ONNX_Tensor, getSlice,     php_ort_tensor_getSlice_arginfo,     ZEND_ACC_PUBLIC)
1815
    PHP_ME(ONNX_Tensor, getData,      php_ort_tensor_getData_arginfo,      ZEND_ACC_PUBLIC)
1816
    PHP_ME(ONNX_Tensor, transpose,    php_ort_tensor_transpose_arginfo,    ZEND_ACC_PUBLIC)
1817

1818
    PHP_ME(ONNX_Tensor_Persistent, from,
1819
        php_ort_tensor_persistent_from_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)    PHP_FE_END
1820
};
1821

1822
zend_function_entry php_ort_tensor_transient_methods[] = {
1823
    PHP_ME(ONNX_Tensor_Transient, __construct,
1824
        php_ort_tensor_transient_construct_arginfo, ZEND_ACC_PUBLIC)
1825
    PHP_ME(ONNX_Tensor, isPersistent, php_ort_tensor_isPersistent_arginfo, ZEND_ACC_PUBLIC)
1826
    PHP_ME(ONNX_Tensor, getName,      php_ort_tensor_getName_arginfo,      ZEND_ACC_PUBLIC)
1827
    PHP_ME(ONNX_Tensor, getType,      php_ort_tensor_getType_arginfo,      ZEND_ACC_PUBLIC)
1828
    PHP_ME(ONNX_Tensor, getTypeName,  php_ort_tensor_getTypeName_arginfo,  ZEND_ACC_PUBLIC)
1829
    PHP_ME(ONNX_Tensor, getShape,     php_ort_tensor_getShape_arginfo,     ZEND_ACC_PUBLIC)
1830
    PHP_ME(ONNX_Tensor, getSlice,     php_ort_tensor_getSlice_arginfo,     ZEND_ACC_PUBLIC)
1831
    PHP_ME(ONNX_Tensor, getData,      php_ort_tensor_getData_arginfo,      ZEND_ACC_PUBLIC)
1832
    PHP_ME(ONNX_Tensor, transpose,    php_ort_tensor_transpose_arginfo,    ZEND_ACC_PUBLIC)
1833

1834
    PHP_ME(ONNX_Tensor_Transient, from,
1835
        php_ort_tensor_transient_from_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
1836
    PHP_FE_END
1837
};
1838

1839
zend_object* php_ort_tensor_create(zend_class_entry *type) {
70,269✔
1840
    php_ort_tensor_t *ort = ecalloc(1,
70,269✔
1841
        sizeof(php_ort_tensor_t) + zend_object_properties_size(type));
1842

1843
    zend_object_std_init(&ort->std, type);
70,269✔
1844

1845
    ort->std.handlers = &php_ort_tensor_handlers;
70,269✔
1846
    ort->object       = NULL;
70,269✔
1847

1848
    return &ort->std;
90,884✔
1849
}
20,615✔
1850

1851
static HashTable* php_ort_tensor_debug(zend_object *zo, int *temp) {
221✔
1852
    php_ort_tensor_t *ort = php_ort_tensor_fetch(zo);
221✔
1853
    HashTable *debug;
221✔
1854

1855
    ALLOC_HASHTABLE(debug);
221✔
1856
    zend_hash_init(debug, 3, NULL, ZVAL_PTR_DTOR, 0);
221✔
1857

1858
    if (!ort->object) {
221✔
1859
        goto __php_ort_tensor_debug_return;
34✔
1860
    }
1861

1862
    zval persistent;
132✔
1863

1864
    ZVAL_BOOL(&persistent,
187✔
1865
        ort->object->owner == PHP_ORT_OWN_HEAP);
1866
    zend_hash_str_add(debug, 
187✔
1867
        "persistent", sizeof("persistent")-1, 
1868
        &persistent);
1869

1870
    zval type;
132✔
1871
    
1872
    ZVAL_LONG(&type, ort->object->type);
187✔
1873
    zend_hash_add(debug,
242✔
1874
        ZSTR_KNOWN(ZEND_STR_TYPE), &type);
187✔
1875

1876
    if (ort->object->name) {
187✔
1877
        zval name;
187✔
1878

1879
        ZVAL_STR_COPY(&name, ort->object->name);
187✔
1880
        zend_hash_add(debug,
242✔
1881
            ZSTR_KNOWN(ZEND_STR_NAME), &name);
187✔
1882
    }
55✔
1883

1884
    zval shape;
132✔
1885

1886
    array_init(&shape);
187✔
1887
    for (int64_t dimension = 0; dimension < ort->object->dimensions; dimension++) {
510✔
1888
        add_next_index_long(
323✔
1889
            &shape, ort->object->shape[dimension]);
323✔
1890
    }
95✔
1891
    zend_hash_str_add(debug,
187✔
1892
        "shape", sizeof("shape")-1, &shape);
1893

1894
__php_ort_tensor_debug_return:
156✔
1895
    *temp = 1;
221✔
1896

1897
    return debug;
286✔
1898
}
65✔
1899

1900
static void php_ort_tensor_write(zend_object* object, zval* offset, zval* value) {
17✔
1901
    php_ort_status_flow(!SUCCESS, {
17✔
1902
        return;
1903
    },
1904
    php_ort_status_tensor_invalidaccess_ce,
1905
    "Tensors are immutable, illegal write operation cannot be performed");
1906
}
5✔
1907

1908
static zval* php_ort_tensor_read(zend_object* object, zval* offset, int type, zval* rv) {
68✔
1909
    php_ort_tensor_t *ort = php_ort_tensor_fetch(object);
68✔
1910
    zend_long index;
68✔
1911

1912
    if (Z_TYPE_P(offset) != IS_LONG) {
68✔
1913
        php_ort_status_flow(!SUCCESS, {
17✔
1914
                ZVAL_UNDEF(rv);
1915
                return rv;
1916
            },
1917
            php_ort_status_tensor_invalidoffset_ce,
1918
            "Tensor element must be an integer");
1919
        return NULL;
1920
    }
1921

1922
    index = Z_LVAL_P(offset);
51✔
1923

1924
    if (index < 0) {
51✔
1925
        // Negative index means offset from the end
1926

1927
        index += ort->object->elements;
17✔
1928
    }
5✔
1929

1930
    php_ort_status_flow(
51✔
1931
        (index >= ort->object->elements),
1932
        {
1933
            ZVAL_UNDEF(rv);
1934
            return rv;
1935
        },
1936
        php_ort_status_tensor_invalidoffset_ce,
1937
        "Tensor element %zd out of range [0, %zd]",
1938
        index, ort->object->elements);
1939

1940
    switch (ort->object->type) {
34✔
1941
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16:
1942
            ZVAL_DOUBLE(rv,
×
1943
                ort_math_float64_from_float16(
1944
                    ((float16*)ort->object->data)[index]));
1945
        break;
×
1946

1947
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32:
1948
            ZVAL_DOUBLE(rv,
×
1949
                ((float32*)ort->object->data)[index]);
1950
        break;
×
1951

1952
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64:
1953
            ZVAL_DOUBLE(rv,
×
1954
                ((float64*)ort->object->data)[index]);
1955
            break;
×
1956

1957
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
1958
            ZVAL_LONG(rv,
×
1959
                ((int8_t*)ort->object->data)[index]);
1960
            break;
×
1961
        
1962
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16:
1963
            ZVAL_LONG(rv,
×
1964
                ((int16_t*)ort->object->data)[index]);
1965
            break;
×
1966

1967
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
24✔
1968
            ZVAL_LONG(rv,
34✔
1969
                ((int32_t*)ort->object->data)[index]);
1970
            break;
34✔
1971

1972
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
1973
            ZVAL_LONG(rv,
×
1974
                ((int64_t*)ort->object->data)[index]);
1975
            break;
×
1976

1977
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
1978
            ZVAL_LONG(rv,
×
1979
                ((uint8_t*)ort->object->data)[index]);
1980
            break;
×
1981
        
1982
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16:
1983
            ZVAL_LONG(rv,
×
1984
                ((uint16_t*)ort->object->data)[index]);
1985
            break;
×
1986
        
1987
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32:
1988
            ZVAL_LONG(rv,
×
1989
                ((uint32_t*)ort->object->data)[index]);
1990
            break;
×
1991

1992
        case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL:
1993
            ZVAL_BOOL(rv,
×
1994
                ((zend_bool*)ort->object->data)[index] ? 1 : 0);
1995
            break;
×
1996

1997
        default: {
1998
            /* unreachable */
1999
        }
2000
            
2001
    }
2002
    return rv;
10✔
2003
}
20✔
2004

2005
static zend_result php_ort_tensor_count(zend_object* object, zend_long *count) {
17✔
2006
    php_ort_tensor_t *ort =
22✔
2007
        php_ort_tensor_fetch(object);
17✔
2008
    *count =
17✔
2009
        ort->object->elements;
17✔
2010
    return SUCCESS;
17✔
2011
}
5✔
2012

2013
void php_ort_tensor_destroy(zend_object *o) {
70,269✔
2014
    php_ort_tensor_t *ort =
90,884✔
2015
        php_ort_tensor_fetch(o);
70,269✔
2016

2017
    ort_tensor_release(ort->object);
70,269✔
2018

2019
    zend_object_std_dtor(o);
70,269✔
2020
}
70,269✔
2021

2022
PHP_MINIT_FUNCTION(ORT_TENSOR)
4,089✔
2023
{
2024
    zend_class_entry ce;
4,089✔
2025

2026
#ifdef ZTS
2027
    php_ort_tensor_mutex = tsrm_mutex_alloc();
2,994✔
2028
#endif
2029

2030
    zend_hash_init(&php_ort_tensors, 16, NULL, php_ort_tensor_del, 1);
4,089✔
2031

2032
    // Setup shared handlers for all tensor types
2033
    memcpy(&php_ort_tensor_handlers,
4,089✔
2034
        zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2035

2036
    php_ort_tensor_handlers.offset = XtOffsetOf(php_ort_tensor_t, std);
4,089✔
2037
    php_ort_tensor_handlers.get_debug_info = php_ort_tensor_debug;
4,089✔
2038
    php_ort_tensor_handlers.free_obj = php_ort_tensor_destroy;
4,089✔
2039
    php_ort_tensor_handlers.read_dimension = php_ort_tensor_read;
4,089✔
2040
    php_ort_tensor_handlers.write_dimension = php_ort_tensor_write;
4,089✔
2041
    php_ort_tensor_handlers.count_elements = php_ort_tensor_count;
4,089✔
2042
    php_ort_tensor_handlers.clone_obj = NULL;
4,089✔
2043

2044
    // Register the interface
2045
    INIT_NS_CLASS_ENTRY(ce, "ORT", "Tensor", php_ort_tensor_interface_methods);
4,089✔
2046
    php_ort_tensor_interface_ce = zend_register_internal_interface(&ce);
4,089✔
2047

2048
    // Register persistent tensor class
2049
    INIT_NS_CLASS_ENTRY(ce, "ORT\\Tensor", "Persistent", php_ort_tensor_persistent_methods);
4,089✔
2050
    php_ort_tensor_persistent_ce = zend_register_internal_class(&ce);
4,089✔
2051
    php_ort_tensor_persistent_ce->create_object = php_ort_tensor_create;
4,089✔
2052
    zend_class_implements(php_ort_tensor_persistent_ce, 1, php_ort_tensor_interface_ce);
4,089✔
2053

2054
    // Register transient tensor class
2055
    INIT_NS_CLASS_ENTRY(ce, "ORT\\Tensor", "Transient", php_ort_tensor_transient_methods);
4,089✔
2056
    php_ort_tensor_transient_ce = zend_register_internal_class(&ce);
4,089✔
2057
    php_ort_tensor_transient_ce->create_object = php_ort_tensor_create;
4,089✔
2058
    zend_class_implements(php_ort_tensor_transient_ce, 1, php_ort_tensor_interface_ce);
4,089✔
2059

2060
#ifdef ZEND_ACC_NOT_SERIALIZABLE
2061
    php_ort_tensor_persistent_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
4,089✔
2062
    php_ort_tensor_transient_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
4,089✔
2063
#else
2064
    php_ort_tensor_persistent_ce->serialize = zend_class_serialize_deny;
2065
    php_ort_tensor_persistent_ce->unserialize = zend_class_unserialize_deny;
2066
    php_ort_tensor_transient_ce->serialize = zend_class_serialize_deny;
2067
    php_ort_tensor_transient_ce->unserialize = zend_class_unserialize_deny;
2068
#endif
2069

2070
    // Register constants on the interface
2071
    zend_declare_class_constant_long(
4,089✔
2072
        php_ort_tensor_interface_ce,
1,095✔
2073
        "UNDEFINED", sizeof("UNDEFINED")-1,
2074
        ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED);
2075

2076
    zend_declare_class_constant_long(
4,089✔
2077
        php_ort_tensor_interface_ce,
1,095✔
2078
        "FLOAT16", sizeof("FLOAT16")-1,
2079
        ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16);
2080

2081
    zend_declare_class_constant_long(
4,089✔
2082
        php_ort_tensor_interface_ce,
1,095✔
2083
        "FLOAT32", sizeof("FLOAT32")-1,
2084
        ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT32);
2085

2086
    zend_declare_class_constant_long(
4,089✔
2087
        php_ort_tensor_interface_ce,
1,095✔
2088
        "FLOAT64", sizeof("FLOAT64")-1,
2089
        ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT64);
2090

2091
    zend_declare_class_constant_long(
4,089✔
2092
        php_ort_tensor_interface_ce,
1,095✔
2093
        "UINT8", sizeof("UINT8")-1,
2094
        ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8);
2095

2096
    zend_declare_class_constant_long(
4,089✔
2097
        php_ort_tensor_interface_ce,
1,095✔
2098
        "INT8", sizeof("INT8")-1,
2099
        ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8);
2100

2101
    zend_declare_class_constant_long(
4,089✔
2102
        php_ort_tensor_interface_ce,
1,095✔
2103
        "UINT16", sizeof("UINT16")-1,
2104
        ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16);
2105

2106
    zend_declare_class_constant_long(
4,089✔
2107
        php_ort_tensor_interface_ce,
1,095✔
2108
        "INT16", sizeof("INT16")-1,
2109
        ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16);
2110

2111
    zend_declare_class_constant_long(
4,089✔
2112
        php_ort_tensor_interface_ce,
1,095✔
2113
        "UINT32", sizeof("UINT32")-1,
2114
        ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32);
2115

2116
    zend_declare_class_constant_long(
4,089✔
2117
        php_ort_tensor_interface_ce,
1,095✔
2118
        "INT32", sizeof("INT32")-1,
2119
        ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32);
2120

2121
    zend_declare_class_constant_long(
4,089✔
2122
        php_ort_tensor_interface_ce,
1,095✔
2123
        "INT64", sizeof("INT64")-1,
2124
        ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64);
2125

2126
    zend_declare_class_constant_long(
4,089✔
2127
        php_ort_tensor_interface_ce,
1,095✔
2128
        "BOOL", sizeof("BOOL")-1,
2129
        ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL);
2130

2131
    return SUCCESS;
4,089✔
2132
}
1,095✔
2133

2134
PHP_MSHUTDOWN_FUNCTION(ORT_TENSOR)
4,089✔
2135
{
2136
    zend_hash_destroy(&php_ort_tensors);
4,089✔
2137

2138
#ifdef ZTS
2139
    tsrm_mutex_free(php_ort_tensor_mutex);
2,994✔
2140
#endif
2141

2142
    return SUCCESS;
4,089✔
2143
}
2144

2145
PHP_RINIT_FUNCTION(ORT_TENSOR)
3,725✔
2146
{
2147
    return SUCCESS;
3,725✔
2148
}
2149

2150
PHP_RSHUTDOWN_FUNCTION(ORT_TENSOR)
3,725✔
2151
{
2152
    return SUCCESS;
3,725✔
2153
}
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