Initial Version

This commit is contained in:
root
2025-12-21 10:09:54 -05:00
commit 2fbddd7dbc
366 changed files with 41999 additions and 0 deletions

76
core/lib/Db/Client.php Normal file
View File

@@ -0,0 +1,76 @@
<?php
namespace KTXC\Db;
use MongoDB\Client as MongoClient;
/**
* Wrapper for MongoDB\Client
* Provides abstraction layer for MongoDB client operations
*/
class Client
{
private MongoClient $client;
/**
* Create a new MongoDB client
*
* @param string $uri Connection URI
* @param array $uriOptions URI options
* @param array $driverOptions Driver options
*/
public function __construct(string $uri = 'mongodb://localhost:27017', array $uriOptions = [], array $driverOptions = [])
{
$this->client = new MongoClient($uri, $uriOptions, $driverOptions);
}
/**
* Select a database
*
* @param string $databaseName Database name
* @param array $options Database options
* @return Database
*/
public function selectDatabase(string $databaseName, array $options = []): Database
{
$mongoDatabase = $this->client->selectDatabase($databaseName, $options);
return new Database($mongoDatabase);
}
/**
* List databases
*/
public function listDatabases(array $options = []): array
{
$databases = [];
foreach ($this->client->listDatabases($options) as $databaseInfo) {
$databases[] = $databaseInfo;
}
return $databases;
}
/**
* Drop a database
*/
public function dropDatabase(string $databaseName, array $options = []): array|object|null
{
return $this->client->dropDatabase($databaseName, $options);
}
/**
* Get the underlying MongoDB Client
* Use sparingly - prefer using wrapper methods
*/
public function getMongoClient(): MongoClient
{
return $this->client;
}
/**
* Magic method to access database as property
*/
public function __get(string $databaseName): Database
{
return $this->selectDatabase($databaseName);
}
}

295
core/lib/Db/Collection.php Normal file
View File

@@ -0,0 +1,295 @@
<?php
namespace KTXC\Db;
use MongoDB\Collection as MongoCollection;
use MongoDB\InsertOneResult;
use MongoDB\UpdateResult;
use MongoDB\DeleteResult;
/**
* Wrapper for MongoDB\Collection
* Provides abstraction layer for MongoDB collection operations
*/
class Collection
{
private MongoCollection $collection;
public function __construct(MongoCollection $collection)
{
$this->collection = $collection;
// Set type map to return plain arrays instead of objects
// This converts BSON types to PHP native types
$this->collection = $collection->withOptions([
'typeMap' => [
'root' => 'array',
'document' => 'array',
'array' => 'array'
]
]);
}
/**
* Find documents in the collection
*
* @param array $filter Query filter
* @param array $options Query options
* @return Cursor
*/
public function find(array $filter = [], array $options = []): Cursor
{
$filter = $this->convertFilter($filter);
/** @var \Iterator $cursor */
$cursor = $this->collection->find($filter, $options);
return new Cursor($cursor);
}
/**
* Find a single document
*
* @param array $filter Query filter
* @param array $options Query options
* @return array|null Returns array with _id as string
*/
public function findOne(array $filter = [], array $options = []): ?array
{
$filter = $this->convertFilter($filter);
$result = $this->collection->findOne($filter, $options);
if ($result === null) {
return null;
}
// Convert to array if it's an object
if (is_object($result)) {
$result = (array) $result;
}
return $this->convertBsonToNative($result);
}
/**
* Insert a single document
*
* @param array|object $document Document to insert
* @param array $options Insert options
* @return InsertOneResult
*/
public function insertOne(array|object $document, array $options = []): InsertOneResult
{
$document = $this->convertDocument($document);
return $this->collection->insertOne($document, $options);
}
/**
* Insert multiple documents
*
* @param array $documents Documents to insert
* @param array $options Insert options
*/
public function insertMany(array $documents, array $options = []): mixed
{
$documents = array_map(fn($doc) => $this->convertDocument($doc), $documents);
return $this->collection->insertMany($documents, $options);
}
/**
* Update a single document
*
* @param array $filter Query filter
* @param array $update Update operations
* @param array $options Update options
* @return UpdateResult
*/
public function updateOne(array $filter, array $update, array $options = []): UpdateResult
{
$filter = $this->convertFilter($filter);
$update = $this->convertDocument($update);
return $this->collection->updateOne($filter, $update, $options);
}
/**
* Update multiple documents
*
* @param array $filter Query filter
* @param array $update Update operations
* @param array $options Update options
* @return UpdateResult
*/
public function updateMany(array $filter, array $update, array $options = []): UpdateResult
{
$filter = $this->convertFilter($filter);
$update = $this->convertDocument($update);
return $this->collection->updateMany($filter, $update, $options);
}
/**
* Delete a single document
*
* @param array $filter Query filter
* @param array $options Delete options
* @return DeleteResult
*/
public function deleteOne(array $filter, array $options = []): DeleteResult
{
$filter = $this->convertFilter($filter);
return $this->collection->deleteOne($filter, $options);
}
/**
* Delete multiple documents
*
* @param array $filter Query filter
* @param array $options Delete options
* @return DeleteResult
*/
public function deleteMany(array $filter, array $options = []): DeleteResult
{
$filter = $this->convertFilter($filter);
return $this->collection->deleteMany($filter, $options);
}
/**
* Count documents matching filter
*
* @param array $filter Query filter
* @param array $options Count options
* @return int
*/
public function countDocuments(array $filter = [], array $options = []): int
{
$filter = $this->convertFilter($filter);
return $this->collection->countDocuments($filter, $options);
}
/**
* Execute aggregation pipeline
*
* @param array $pipeline Aggregation pipeline
* @param array $options Aggregation options
* @return Cursor
*/
public function aggregate(array $pipeline, array $options = []): Cursor
{
/** @var \Iterator $cursor */
$cursor = $this->collection->aggregate($pipeline, $options);
return new Cursor($cursor);
}
/**
* Create an index
*
* @param array $key Index specification
* @param array $options Index options
* @return string Index name
*/
public function createIndex(array $key, array $options = []): string
{
return $this->collection->createIndex($key, $options);
}
/**
* Drop the collection
*/
public function drop(): array|object|null
{
return $this->collection->drop();
}
/**
* Get collection name
*/
public function getCollectionName(): string
{
return $this->collection->getCollectionName();
}
/**
* Get database name
*/
public function getDatabaseName(): string
{
return $this->collection->getDatabaseName();
}
/**
* Convert ObjectId instances in filter to MongoDB ObjectId
*/
private function convertFilter(array $filter): array
{
return $this->convertArray($filter);
}
/**
* Convert ObjectId instances in document to MongoDB ObjectId
*/
private function convertDocument(array|object $document): array|object
{
if (is_array($document)) {
return $this->convertArray($document);
}
return $document;
}
/**
* Recursively convert ObjectId and UTCDateTime instances
*/
private function convertArray(array $data): array
{
foreach ($data as $key => $value) {
if ($value instanceof ObjectId) {
$data[$key] = $value->toBSON();
} elseif ($value instanceof UTCDateTime) {
$data[$key] = $value->toBSON();
} elseif (is_array($value)) {
$data[$key] = $this->convertArray($value);
}
}
return $data;
}
/**
* Get the underlying MongoDB Collection
* Use sparingly - prefer using wrapper methods
*/
public function getMongoCollection(): MongoCollection
{
return $this->collection;
}
/**
* Convert BSON objects to native PHP types
* Handles ObjectId, UTCDateTime, and other BSON types
*/
private function convertBsonToNative(mixed $data): mixed
{
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->convertBsonToNative($value);
}
return $data;
}
if (is_object($data)) {
// Convert MongoDB BSON ObjectId to string
if ($data instanceof \MongoDB\BSON\ObjectId) {
return (string) $data;
}
// Convert MongoDB BSON UTCDateTime to string or DateTime
if ($data instanceof \MongoDB\BSON\UTCDateTime) {
return (string) $data->toDateTime()->format('c');
}
// Convert other objects to arrays recursively
if (method_exists($data, 'bsonSerialize')) {
return $this->convertBsonToNative($data->bsonSerialize());
}
return (array) $data;
}
return $data;
}
}

86
core/lib/Db/Cursor.php Normal file
View File

@@ -0,0 +1,86 @@
<?php
namespace KTXC\Db;
use Iterator;
use IteratorAggregate;
use Traversable;
/**
* Wrapper for MongoDB Cursor
* Provides abstraction layer for MongoDB cursor operations
* Automatically converts BSON types to native PHP types
*/
class Cursor implements IteratorAggregate
{
private Iterator $cursor;
public function __construct(Iterator $cursor)
{
$this->cursor = $cursor;
}
/**
* Convert cursor to array with BSON types converted to native PHP types
*/
public function toArray(): array
{
$result = iterator_to_array($this->cursor);
return $this->convertBsonToNative($result);
}
/**
* Get iterator for foreach loops
* Note: Items will be returned as-is (may contain BSON objects)
* Use toArray() if you need full conversion
*/
public function getIterator(): Traversable
{
return $this->cursor;
}
/**
* Get underlying MongoDB cursor
*/
public function getMongoCursor(): Iterator
{
return $this->cursor;
}
/**
* Convert BSON objects to native PHP types
* Handles ObjectId, UTCDateTime, and other BSON types
*/
private function convertBsonToNative(mixed $data): mixed
{
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->convertBsonToNative($value);
}
return $data;
}
if (is_object($data)) {
// Convert MongoDB BSON ObjectId to string
if ($data instanceof \MongoDB\BSON\ObjectId) {
return (string) $data;
}
// Convert MongoDB BSON UTCDateTime to ISO8601 string
if ($data instanceof \MongoDB\BSON\UTCDateTime) {
return $data->toDateTime()->format('c');
}
// Convert other objects to arrays recursively
if (method_exists($data, 'bsonSerialize')) {
return $this->convertBsonToNative($data->bsonSerialize());
}
// Convert stdClass and other objects to array
$array = (array) $data;
return $this->convertBsonToNative($array);
}
return $data;
}
}

97
core/lib/Db/DataStore.php Normal file
View File

@@ -0,0 +1,97 @@
<?php
namespace KTXC\Db;
use DI\Attribute\Inject;
/**
* DataStore provides access to MongoDB database operations
* Uses composition pattern with Database wrapper
*/
class DataStore
{
protected array $configuration;
protected Client $client;
protected Database $database;
public function __construct(#[Inject('database')] array $configuration)
{
$this->configuration = $configuration;
$uri = $configuration['uri'];
$databaseName = $configuration['database'];
$options = $configuration['options'] ?? [];
$driverOptions = $configuration['driverOptions'] ?? [];
$this->client = new Client($uri, $options, $driverOptions);
$this->database = $this->client->selectDatabase($databaseName, $options);
}
/**
* Select a collection from the database
*
* @param string $collectionName Collection name
* @param array $options Collection options
* @return Collection
*/
public function selectCollection(string $collectionName, array $options = []): Collection
{
return $this->database->selectCollection($collectionName, $options);
}
/**
* Get the underlying Database instance
*/
public function getDatabase(): Database
{
return $this->database;
}
/**
* Get the Client instance
*/
public function getClient(): Client
{
return $this->client;
}
/**
* List all collections
*/
public function listCollections(array $options = []): array
{
return $this->database->listCollections($options);
}
/**
* Create a collection
*/
public function createCollection(string $collectionName, array $options = []): Collection
{
return $this->database->createCollection($collectionName, $options);
}
/**
* Drop a collection
*/
public function dropCollection(string $collectionName, array $options = []): array|object
{
return $this->database->dropCollection($collectionName, $options);
}
/**
* Get database name
*/
public function getDatabaseName(): string
{
return $this->database->getDatabaseName();
}
/**
* Magic method to access collection as property
*/
public function __get(string $collectionName): Collection
{
return $this->selectCollection($collectionName);
}
}

104
core/lib/Db/Database.php Normal file
View File

@@ -0,0 +1,104 @@
<?php
namespace KTXC\Db;
use MongoDB\Database as MongoDatabase;
/**
* Wrapper for MongoDB\Database
* Provides abstraction layer for MongoDB database operations
*/
class Database
{
private MongoDatabase $database;
public function __construct(MongoDatabase $database)
{
$this->database = $database;
}
/**
* Select a collection
*
* @param string $collectionName Collection name
* @param array $options Collection options
* @return Collection
*/
public function selectCollection(string $collectionName, array $options = []): Collection
{
$mongoCollection = $this->database->selectCollection($collectionName, $options);
return new Collection($mongoCollection);
}
/**
* List collections
*/
public function listCollections(array $options = []): array
{
$collections = [];
foreach ($this->database->listCollections($options) as $collectionInfo) {
$collections[] = $collectionInfo;
}
return $collections;
}
/**
* Drop the database
*/
public function drop(array $options = []): array|object|null
{
return $this->database->drop($options);
}
/**
* Get database name
*/
public function getDatabaseName(): string
{
return $this->database->getDatabaseName();
}
/**
* Create a collection
*/
public function createCollection(string $collectionName, array $options = []): Collection|null
{
$mongoCollection = $this->database->createCollection($collectionName, $options);
return $mongoCollection ? new Collection($mongoCollection) : null;
}
/**
* Drop a collection
*/
public function dropCollection(string $collectionName, array $options = []): array|object|null
{
return $this->database->dropCollection($collectionName, $options);
}
/**
* Execute a database command
*/
public function command(array|object $command, array $options = []): Cursor
{
/** @var \Iterator $cursor */
$cursor = $this->database->command($command, $options);
return new Cursor($cursor);
}
/**
* Get the underlying MongoDB Database
* Use sparingly - prefer using wrapper methods
*/
public function getMongoDatabase(): MongoDatabase
{
return $this->database;
}
/**
* Magic method to access collection as property
*/
public function __get(string $collectionName): Collection
{
return $this->selectCollection($collectionName);
}
}

71
core/lib/Db/ObjectId.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
namespace KTXC\Db;
use MongoDB\BSON\ObjectId as MongoObjectId;
/**
* Wrapper for MongoDB\BSON\ObjectId
* Provides abstraction layer for MongoDB ObjectId handling
*/
class ObjectId
{
private MongoObjectId $objectId;
/**
* Create a new ObjectId
*
* @param string|MongoObjectId|null $id Optional ID string or MongoDB ObjectId
*/
public function __construct(string|MongoObjectId|null $id = null)
{
if ($id instanceof MongoObjectId) {
$this->objectId = $id;
} elseif (is_string($id)) {
$this->objectId = new MongoObjectId($id);
} else {
$this->objectId = new MongoObjectId();
}
}
/**
* Get the string representation of the ObjectId
*/
public function __toString(): string
{
return (string) $this->objectId;
}
/**
* Get the underlying MongoDB ObjectId
* Used internally when interacting with MongoDB driver
*/
public function toBSON(): MongoObjectId
{
return $this->objectId;
}
/**
* Get the timestamp from the ObjectId
*/
public function getTimestamp(): int
{
return $this->objectId->getTimestamp();
}
/**
* Create ObjectId from string
*/
public static function fromString(string $id): self
{
return new self($id);
}
/**
* Check if a string is a valid ObjectId
*/
public static function isValid(string $id): bool
{
return MongoObjectId::isValid($id);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace KTXC\Db;
use MongoDB\BSON\UTCDateTime as MongoUTCDateTime;
use DateTimeInterface;
/**
* Wrapper for MongoDB\BSON\UTCDateTime
* Provides abstraction layer for MongoDB datetime handling
*/
class UTCDateTime
{
private MongoUTCDateTime|string $dateTime;
/**
* Create a new UTCDateTime
*
* @param int|DateTimeInterface|null $milliseconds Milliseconds since epoch, or DateTime object
*/
public function __construct(int|DateTimeInterface|null $milliseconds = null)
{
// Check if MongoDB extension is loaded
if (class_exists(MongoUTCDateTime::class)) {
$this->dateTime = new MongoUTCDateTime($milliseconds);
} else {
// Fallback for environments without MongoDB extension (testing, linting)
$this->dateTime = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))->format(DATE_ATOM);
}
}
/**
* Get the string representation
*/
public function __toString(): string
{
if ($this->dateTime instanceof MongoUTCDateTime) {
return $this->dateTime->toDateTime()->format(DATE_ATOM);
}
return $this->dateTime;
}
/**
* Get the underlying MongoDB UTCDateTime or fallback string
* Used internally when interacting with MongoDB driver
*/
public function toBSON(): MongoUTCDateTime|string
{
return $this->dateTime;
}
/**
* Convert to PHP DateTime
*/
public function toDateTime(): \DateTimeImmutable
{
if ($this->dateTime instanceof MongoUTCDateTime) {
return \DateTimeImmutable::createFromMutable($this->dateTime->toDateTime());
}
return new \DateTimeImmutable($this->dateTime);
}
/**
* Get milliseconds since epoch
*/
public function toMilliseconds(): int
{
if ($this->dateTime instanceof MongoUTCDateTime) {
return (int) $this->dateTime;
}
return (int) ((new \DateTimeImmutable($this->dateTime))->getTimestamp() * 1000);
}
/**
* Create from DateTime
*/
public static function fromDateTime(DateTimeInterface $dateTime): self
{
return new self($dateTime);
}
/**
* Create current timestamp
*/
public static function now(): self
{
return new self();
}
}