Skip to content

Instantly share code, notes, and snippets.

@mikeschinkel
Created July 9, 2024 01:15
Show Gist options
  • Save mikeschinkel/2e7ac67d6948c2774b381599dfa339aa to your computer and use it in GitHub Desktop.
Save mikeschinkel/2e7ac67d6948c2774b381599dfa339aa to your computer and use it in GitHub Desktop.
PHP Classes Statics\MemStream and Statics\MemCache (NOT memcached-related.)
<?php
declare(strict_types=1);
spl_autoload_register(function (string $class) {
if (preg_match("#^Statics\\\Mem(Cache|Stream)$#", $class)) {
$class = str_replace('\\', '/', $class);
include dirname(__DIR__) . "/{$class}.php";
}
});
\Statics\MemCache::runExample();
<?php
declare(strict_types=1);
namespace Statics;
/**
* Class MemCache
*
* Handles static memory caching — NOT TO BE CONFUSED with `memcached`.
*/
class MemCache {
/**
* Static property to hold items stored in memory and indexed by handle.
*
* @var array
*/
private static array $mem = [];
/**
* Runs an example showing how to use static mem stream.
*
* @return void
*/
public static function runExample(): void {
MemStream::init();
$handle = MemCache::newHandle();
$fp = fopen( MemCache::getStreamUrl( $handle ), "r+" );
fwrite( $fp, "line1\n" );
fwrite( $fp, "line2\n" );
fwrite( $fp, "line3\n" );
rewind( $fp );
while ( ! feof( $fp ) ) {
echo fgets( $fp );
}
fclose( $fp );
var_dump( MemCache::getValue( $handle ) );
MemCache::closeHandle( $handle );
}
/**
* Generates a new unique handle (key) into the internal self::$mem array.
*
* @return int The unique handle.
*/
public static function newHandle(): int {
do {
// Generate a random number between 1,000,000 and 9,999,999
$handle = mt_rand( 1000000, 9999999 );
} while ( array_key_exists( $handle, self::$mem ) ); // Ensure the handle is unique
// Add the handle to the memory array
self::$mem[ $handle ] = '';
// Return the unique handle
return $handle;
}
/**
* Gets the URL to use with fopen(), Phar(), etc.
*
* @param int $handle The handle for which to get the URL.
*
* @return string The URL associated with the handle.
*/
public static function getStreamUrl( int $handle ): string {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
return "staticmem://{$handle}";
}
/**
* Sets the value stored by handle in static memory.
*
* @param int $handle The handle for which to set the value.
* @param string $value The value to be stored.
*
* @return void
*/
public static function setValue( int $handle, string $value ): void {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
self::$mem[ $handle ] = $value;
}
/**
* Concatenates to a value stored by handle in static memory.
*
* @param int $handle The handle for which to concatenate the value.
* @param string $value The value to concatenate.
*
* @return void
*/
public static function concatValue( int $handle, string $value ): void {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
self::$mem[ $handle ] .= $value;
}
/**
* Gets the value stored by handle in static memory.
*
* @param int $handle The handle for which to get the value.
*
* @return string|null The value stored or null if not found.
*/
public static function getValue( int $handle ): ?string {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
return self::$mem[ $handle ];
}
/**
* Gets a substring of the value stored by handle in static memory.
*
* @param int $handle The handle for which to get the substring.
* @param int $pos The start position of the substring.
* @param int $count The length of the substring.
*
* @return string|null The substring or null if not found.
*/
public static function getSubstrValue( int $handle, int $pos, int $count ): ?string {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
return substr( self::$mem[ $handle ], $pos, $count );
}
/**
* Gets the string length of the value stored for the handle in static memory.
*
* @param int $handle The handle for which to get the string length.
*
* @return int The length of the stored value.
*/
public static function getSize( int $handle ): int {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
return strlen( self::$mem[ $handle ] );
}
/**
* "Closes" the handle and releases related memory.
*
* @param int $handle The handle to be closed.
*
* @return void
*/
public static function closeHandle( int $handle ): void {
/** @noinspection PhpUnhandledExceptionInspection */
self::checkHandle( $handle );
unset( self::$mem[ $handle ] );
}
/**
* Checks if the handle is valid.
*
* @param int $handle The handle to check.
*
* @return void
* @throws \Exception If the handle is not valid.
*/
public static function checkHandle( int $handle ): void {
if ( ! array_key_exists( $handle, self::$mem ) ) {
/** @noinspection PhpUnhandledExceptionInspection */
throw new \Exception( "Invalid static memory handle: {$handle}" );
}
}
/**
* Returns the internal array (for debugging).
*
* @return array The internal memory array.
*/
public static function getMem(): array {
return self::$mem;
}
}
<?php
declare(strict_types=1);
namespace Statics;
/**
* Class MemStream
*
* A class to handle stream operations on static memory.
*/
class MemStream {
/**
* @var mixed $context Stream context set by stream code.
*/
public mixed $context;
/**
* @var int $position Current position in the stream.
*/
private int $position;
/**
* @var int $handle Handle for the static memory.
*/
private int $handle;
/**
* Opens a stream.
*
* @param string $path The path to open.
* @param string $mode The mode in which to open the stream.
* @param int $options Additional options.
* @param string|null $opened_path Path that was opened.
* @return bool True on success, false on failure.
* @throws \Exception If the handle is invalid.
*/
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool {
$url = parse_url($path);
$this->handle = (int)$url["host"];
try {
MemCache::checkHandle( $this->handle );
} catch (\Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw new \Exception("Invalid staticmem stream handle: '{$path}'; expected 'staticmem://<valid_int_handle>'");
}
$this->position = 0;
return true;
}
/**
* Reads from the stream.
*
* @param int $count Number of bytes to read.
* @return string The data read from the stream.
*/
public function stream_read(int $count): string {
$result = MemCache::getSubstrValue($this->handle, $this->position, $count);
$this->position += strlen($result);
return $result;
}
/**
* Writes to the stream.
*
* @param string $data The data to write.
* @return int The number of bytes written.
*/
public function stream_write(string $data): int {
$value=MemCache::getValue($this->handle);
$written=strlen($data);
MemCache::setValue($this->handle,
substr($value, 0, $this->position)
. $data
. substr($value, $this->position += $written)
);
return $written;
}
/**
* Returns the current position in the stream.
*
* @return int The current position.
*/
public function stream_tell():int {
return $this->position;
}
/**
* Checks if the end of the stream has been reached.
*
* @return bool True if end of stream, false otherwise.
*/
public function stream_eof():bool {
return $this->position >= MemCache::getSize($this->handle);
}
/**
* Seeks to a position in the stream.
*
* @param int $offset The offset to seek to.
* @param int $mode The mode to use for seeking.
* @return bool True on success, false on failure.
*/
public function stream_seek($offset, $mode):bool {
$size = MemCache::getSize($this->handle);
switch ($mode) {
case SEEK_SET: $newPos = $offset; break;
case SEEK_CUR: $newPos = $this->position + $offset; break;
case SEEK_END: $newPos = $size + $offset; break;
default: return false;
}
$result = ($newPos >=0 && $newPos <=$size);
if ($result) {
$this->position = $newPos;
}
return $result;
}
/**
* Registers the stream wrapper protocol.
*
* @return void
* @throws \Exception If registration fails.
*/
public static function init(): void {
if (!stream_wrapper_register("staticmem", "Statics\\MemStream")) {
/** @noinspection PhpUnhandledExceptionInspection */
throw new \Exception("Failed to register staticmem:// protocol");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment