Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,24 @@ ReflectionMethod
- `setDeprecated(bool $isDeprecated = true): void` Declares this method as deprecated/non-deprecated
- `redefine(\Closure $newCode): void` Redefines this method with a closure definition
- `getOpCodes(): iterable`: \[WIP\] Returns the list of opcodes for this method

ObjectStore API
-------------

Every object in PHP has it's own unique identifier, which can be received via `spl_object_id($object)`. Sometimes
we are looking for the way to get an object by it's identifier. Unfortunately, PHP doesn't provide such an API, whereas
internally there is an instance of `zend_objects_store` structure which is stored in the global `executor_globals`
variable (aka EG).

This library provides an `ObjectStore` API via `Core::$executor->objectStore` which implements an `ArrayAccess` and
`Countable` interface. This means that you can get any existing object by accessing this store with object handle:

```php
use ZEngine\Core;

$instance = new stdClass();
$handle = spl_object_id($instance);

$objectEntry = Core::$executor->objectStore[$handle];
var_dump($objectEntry);
```
34 changes: 24 additions & 10 deletions include/engine_x64_nts.h
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,22 @@ struct _zend_compiler_globals {
HashTable *delayed_autoloads; /* Usually empty */
};

#ifdef ZEND_WIN32
typedef struct _OSVERSIONINFOEXA {
uint32_t dwOSVersionInfoSize;
uint32_t dwMajorVersion;
uint32_t dwMinorVersion;
uint32_t dwBuildNumber;
uint32_t dwPlatformId;
char szCSDVersion[128];
uint16_t wServicePackMajor;
uint16_t wServicePackMinor;
uint16_t wSuiteMask;
char wProductType;
char wReserved;
} OSVERSIONINFOEX;
#endif

struct _zend_executor_globals {
zval uninitialized_zval;
zval error_zval;
Expand Down Expand Up @@ -995,10 +1011,9 @@ struct _zend_executor_globals {
zend_bool timed_out;
zend_long hard_timeout;

// TODO: Windows-dependent code
// #ifdef ZEND_WIN32
// OSVERSIONINFOEX windows_version_info;
// #endif
#ifdef ZEND_WIN32
OSVERSIONINFOEX windows_version_info;
#endif

HashTable regular_list;
HashTable persistent_list;
Expand Down Expand Up @@ -1041,10 +1056,9 @@ struct _zend_executor_globals {

void *saved_fpu_cw_ptr;

// TODO: Platform-dependent code
// #if XPFPA_HAVE_CW
// XPFPA_CW_DATATYPE saved_fpu_cw;
// #endif
#ifdef XPFPA_HAVE_CW
XPFPA_CW_DATATYPE saved_fpu_cw;
#endif

zend_function trampoline;
zend_op call_trampoline_op;
Expand All @@ -1057,10 +1071,10 @@ struct _zend_executor_globals {
};
typedef struct _zend_executor_globals zend_executor_globals;

// #ifndef ZTS
#ifndef ZTS
ZEND_API zend_executor_globals executor_globals;
ZEND_API struct _zend_compiler_globals compiler_globals;
// #endif
#endif


/**
Expand Down
27 changes: 26 additions & 1 deletion src/Macro/DefinitionLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,32 @@ class DefinitionLoader extends PhpStreamFilter
*/
public function filter($in, $out, &$consumed, $closing)
{
/** Simple pattern to match if(n?)def..endif constructions */
static $pattern = '/^#(ifn?def) +(.*?)\n([\s\S]*?)(#endif)/m';

while ($bucket = stream_bucket_make_writeable($in)) {
$this->data .= $bucket->data;
}

if ($closing || feof($this->stream)) {
$consumed = strlen($this->data);

$macros = $this->resolveSystemMacros();
// Now we emulate resolution of ifdef..endif constructions
$transformedData = $this->data;
$transformedData = preg_replace_callback($pattern, function (array $matches) use ($macros): string {
[, $keyword, $macro, $body] = $matches;
if ($keyword === 'ifdef' && !isset($macros[$macro])) {
$body = '';
} elseif ($keyword === 'ifndef' && isset($macros[$macro])) {
$body = '';
}

return $body;
}, $transformedData);

// Simple macros resolving via strtr
$transformedData = strtr($this->data, $this->resolveSystemMacros());
$transformedData = strtr($transformedData, $macros);

$bucket = stream_bucket_new($this->stream, $transformedData);
stream_bucket_append($out, $bucket);
Expand Down Expand Up @@ -98,6 +115,14 @@ private function resolveSystemMacros(): array
'ZEND_LIBRARY_NAME' => $isWindowsPlatform ? 'php7.dll' : '',
];

if ($isWindowsPlatform) {
$macros['ZEND_WIN32'] = '1';
}

if ($isThreadSafe) {
$macros['ZTS'] = '1';
}

return $macros;
}
}
9 changes: 9 additions & 0 deletions src/System/Executor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use FFI\CData;
use ZEngine\Reflection\ReflectionValue;
use ZEngine\Type\HashTable;
use ZEngine\Type\ObjectEntry;

class Executor
{
Expand All @@ -32,6 +33,13 @@ class Executor
*/
public HashTable $functionTable;

/**
* Represents the global object storage
*
* @var ObjectStore|ObjectEntry[]
*/
public ObjectStore $objectStore;

/**
* Holds an internal pointer to the executor_globals structure
*/
Expand All @@ -42,6 +50,7 @@ public function __construct(CData $pointer)
$this->pointer = $pointer;
$this->classTable = new HashTable($pointer->class_table);
$this->functionTable = new HashTable($pointer->function_table);
$this->objectStore = new ObjectStore($pointer->objects_store);
}

/**
Expand Down
125 changes: 125 additions & 0 deletions src/System/ObjectStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php
/**
* Z-Engine framework
*
* @copyright Copyright 2019, Lisachenko Alexander <lisachenko.it@gmail.com>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*
*/
declare(strict_types=1);

namespace ZEngine\System;

use ArrayAccess;
use Countable;
use FFI\CData;
use ZEngine\Core;
use ZEngine\Type\ObjectEntry;

final class ObjectStore implements Countable, ArrayAccess
{
/**
* @see zend_objects_API.h:OBJ_BUCKET_INVALID macro
*/
private const OBJ_BUCKET_INVALID = 1<<0;

/**
* Holds an internal pointer to the EG(objects_store)
*/
private CData $pointer;

public function __construct(CData $pointer)
{
$this->pointer = $pointer;
}

/**
* @inheritDoc
*/
public function count(): int
{
return $this->pointer->top - 1;
}

/**
* @inheritDoc
*/
public function offsetExists($offset): bool
{
$isValidOffset = ($offset >= 0) && ($offset < $this->pointer->top);
$isExists = $isValidOffset && $this->isObjectValid($this->pointer->object_buckets[$offset]);

return $isExists;
}

/**
* Returns an object from the storage by it's id or null if this object was released
*
* @param int $offset Identifier of object
*
* @see spl_object_id()
*/
public function offsetGet($offset): ?ObjectEntry
{
if (!\is_int($offset)) {
throw new \InvalidArgumentException('Object identifier should be an integer');
}
if ($offset < 0 || $offset > $this->pointer->top - 1) {
// We use -2 because exception object also increments index by one
throw new \OutOfBoundsException("Index {$offset} is out of bounds 0.." . ($this->pointer->top - 2));
}
$object = $this->pointer->object_buckets[$offset];

// Object can be invalid, for that case we should return null
if (!$this->isObjectValid($object)) {
return null;
}

$objectEntry = ObjectEntry::fromCData($object);

return $objectEntry;
}

/**
* @inheritDoc
*/
public function offsetSet($offset, $value): void
{
throw new \LogicException('Object store is read-only structure');
}

/**
* @inheritDoc
*/
public function offsetUnset($offset): void
{
throw new \LogicException('Object store is read-only structure');
}

/**
* Returns the free head (aka next handle)
*/
public function nextHandle(): int
{
return $this->pointer->free_list_head;
}

/**
* Checks if the given object pointer is valid or not
*
* @see zend_objects_API.h:IS_OBJ_VALID macro
*/
private function isObjectValid(?CData $objectPointer): bool
{
if ($objectPointer === null) {
return false;
}

$rawPointer = Core::cast('zend_uintptr_t', $objectPointer);
$isValid = ($rawPointer->cdata & self::OBJ_BUCKET_INVALID) === 0;

return $isValid;
}
}
15 changes: 15 additions & 0 deletions src/Type/ObjectEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use FFI\CData;
use ZEngine\Core;
use ZEngine\Reflection\ReflectionClass;
use ZEngine\Reflection\ReflectionValue;

/**
* Class ObjectEntry represents an object instance in PHP
Expand Down Expand Up @@ -99,6 +100,20 @@ public function setHandle(int $newHandle): void
$this->pointer->handle = $newHandle;
}

/**
* Returns a PHP instance of object, associated with this entry
*/
public function getNativeValue(): object
{
$entry = ReflectionValue::newEntry(ReflectionValue::IS_OBJECT, $this->pointer[0]);
$entry->getNativeValue($realObject);

// TODO: Incapsulate memory management into ReflectionValue->release() method
Core::free($entry->getRawValue());

return $realObject;
}

/**
* This method returns a dumpable representation of internal value to prevent segfault
*/
Expand Down
Loading