diff --git a/CHANGELOG.md b/CHANGELOG.md index d56082b..1e80440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **SMELL-013: Migrated all classes to `CrazyGoat\ZVec\` namespace with PSR-4 autoloading** (#94) + - All library classes now live under `CrazyGoat\ZVec\` namespace + - Global class names preserved via `class_alias()` for backward compatibility + - Use `CrazyGoat\ZVec\ZVec` with `use` statement or keep using global `ZVec` — both work + - Added `tests/test_namespace_bc.phpt` to verify both namespace styles + - Removed `classmap` from `composer.json` — PSR-4 autoloading now handles everything - **SMELL-009: Added `queryWithReranker()` method for type-safe reranked queries** (#90) - New `queryWithReranker()` method on `ZVec` returns `ZVecRerankedDoc[]` — static analysis tools can infer the correct return type - Supports `string|ZVecVectorQuery` as field name, with all existing query parameters @@ -16,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated +- **SMELL-013: Global namespace class names are deprecated** (#94) + - Use `use CrazyGoat\ZVec\ZVec;` instead of global `ZVec` + - Global aliases remain for backward compatibility but may be removed in a future major version - **SMELL-009: The `$reranker` parameter on `query()` and `queryFp64()` is deprecated** (#90) - Passing `$reranker` to `query()` now triggers `E_USER_DEPRECATED` and delegates to `queryWithReranker()` - Existing code continues to work (backward compatible) but should migrate to `queryWithReranker()` diff --git a/composer.json b/composer.json index f3ffa42..9c03ab4 100644 --- a/composer.json +++ b/composer.json @@ -15,10 +15,7 @@ "autoload": { "psr-4": { "CrazyGoat\\ZVec\\": "src/" - }, - "classmap": [ - "src/" - ] + } }, "bin": [ "bin/zvec-install" diff --git a/src/ZVec.php b/src/ZVec.php index ed08342..db786e7 100644 --- a/src/ZVec.php +++ b/src/ZVec.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; require_once __DIR__ . '/ZVecException.php'; @@ -14,6 +18,10 @@ require_once __DIR__ . '/ZVecGroupByVectorQuery.php'; require_once __DIR__ . '/ZVecSchema.php'; require_once __DIR__ . '/ZVecDoc.php'; +require_once __DIR__ . '/ZVecReRanker.php'; +require_once __DIR__ . '/ZVecRerankedDoc.php'; +require_once __DIR__ . '/ZVecRrfReRanker.php'; +require_once __DIR__ . '/ZVecWeightedReRanker.php'; class ZVec { @@ -1644,3 +1652,20 @@ public function getFieldSchema(string $fieldName): ZVecFieldSchema return new ZVecFieldSchema($out); } } + +// Backward-compatible aliases for global namespace usage +class_alias(ZVec::class, 'ZVec'); +class_alias(ZVecException::class, 'ZVecException'); +class_alias(ZVecSchema::class, 'ZVecSchema'); +class_alias(ZVecDoc::class, 'ZVecDoc'); +class_alias(ZVecCollectionOptions::class, 'ZVecCollectionOptions'); +class_alias(ZVecCollectionStats::class, 'ZVecCollectionStats'); +class_alias(ZVecFieldSchema::class, 'ZVecFieldSchema'); +class_alias(ZVecIndexParams::class, 'ZVecIndexParams'); +\class_alias(ZVecQueryInterface::class, 'ZVecQueryInterface'); +class_alias(ZVecVectorQuery::class, 'ZVecVectorQuery'); +class_alias(ZVecGroupByVectorQuery::class, 'ZVecGroupByVectorQuery'); +\class_alias(ZVecReRanker::class, 'ZVecReRanker'); +class_alias(ZVecRerankedDoc::class, 'ZVecRerankedDoc'); +class_alias(ZVecRrfReRanker::class, 'ZVecRrfReRanker'); +class_alias(ZVecWeightedReRanker::class, 'ZVecWeightedReRanker'); diff --git a/src/ZVecCollectionOptions.php b/src/ZVecCollectionOptions.php index 3b7e95a..35a415b 100644 --- a/src/ZVecCollectionOptions.php +++ b/src/ZVecCollectionOptions.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; class ZVecCollectionOptions diff --git a/src/ZVecCollectionStats.php b/src/ZVecCollectionStats.php index ccad2cd..b29ddc2 100644 --- a/src/ZVecCollectionStats.php +++ b/src/ZVecCollectionStats.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecCollectionStats diff --git a/src/ZVecDoc.php b/src/ZVecDoc.php index 44d0895..4dcdd6f 100644 --- a/src/ZVecDoc.php +++ b/src/ZVecDoc.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecDoc diff --git a/src/ZVecException.php b/src/ZVecException.php index 8f8529a..b615532 100644 --- a/src/ZVecException.php +++ b/src/ZVecException.php @@ -2,6 +2,11 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use RuntimeException; +use Throwable; + if (extension_loaded('zvec')) return; class ZVecException extends RuntimeException diff --git a/src/ZVecFieldSchema.php b/src/ZVecFieldSchema.php index 2f81350..b10dbe2 100644 --- a/src/ZVecFieldSchema.php +++ b/src/ZVecFieldSchema.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecFieldSchema diff --git a/src/ZVecGroupByVectorQuery.php b/src/ZVecGroupByVectorQuery.php index e0a6d31..8ac3e38 100644 --- a/src/ZVecGroupByVectorQuery.php +++ b/src/ZVecGroupByVectorQuery.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecGroupByVectorQuery implements ZVecQueryInterface diff --git a/src/ZVecIndexParams.php b/src/ZVecIndexParams.php index 23280fb..fb6cdc5 100644 --- a/src/ZVecIndexParams.php +++ b/src/ZVecIndexParams.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecIndexParams diff --git a/src/ZVecQueryInterface.php b/src/ZVecQueryInterface.php index f95447e..7644994 100644 --- a/src/ZVecQueryInterface.php +++ b/src/ZVecQueryInterface.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; interface ZVecQueryInterface diff --git a/src/ZVecReRanker.php b/src/ZVecReRanker.php index 8623d74..c1fdfa2 100644 --- a/src/ZVecReRanker.php +++ b/src/ZVecReRanker.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; /** diff --git a/src/ZVecRerankedDoc.php b/src/ZVecRerankedDoc.php index 24bca47..6988e31 100644 --- a/src/ZVecRerankedDoc.php +++ b/src/ZVecRerankedDoc.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; /** diff --git a/src/ZVecRrfReRanker.php b/src/ZVecRrfReRanker.php index 521ca84..03a730f 100644 --- a/src/ZVecRrfReRanker.php +++ b/src/ZVecRrfReRanker.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; require_once __DIR__ . '/ZVecReRanker.php'; diff --git a/src/ZVecSchema.php b/src/ZVecSchema.php index 53f3a11..778ed0a 100644 --- a/src/ZVecSchema.php +++ b/src/ZVecSchema.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecSchema diff --git a/src/ZVecVectorQuery.php b/src/ZVecVectorQuery.php index fa0bb0d..0912a13 100644 --- a/src/ZVecVectorQuery.php +++ b/src/ZVecVectorQuery.php @@ -2,6 +2,10 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + +use FFI; + if (extension_loaded('zvec')) return; class ZVecVectorQuery implements ZVecQueryInterface diff --git a/src/ZVecWeightedReRanker.php b/src/ZVecWeightedReRanker.php index 8b370fc..22d4145 100644 --- a/src/ZVecWeightedReRanker.php +++ b/src/ZVecWeightedReRanker.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; require_once __DIR__ . '/ZVec.php'; diff --git a/src/embeddings/EmbeddingInterfaces.php b/src/embeddings/EmbeddingInterfaces.php index 0563939..4e663c7 100644 --- a/src/embeddings/EmbeddingInterfaces.php +++ b/src/embeddings/EmbeddingInterfaces.php @@ -2,8 +2,12 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; +require_once __DIR__ . '/../ZVecException.php'; + /** * Interface for dense embedding functions. * Converts text input into dense vector representations. diff --git a/src/embeddings/OpenAIDenseEmbedding.php b/src/embeddings/OpenAIDenseEmbedding.php index 36dc641..455e0fe 100644 --- a/src/embeddings/OpenAIDenseEmbedding.php +++ b/src/embeddings/OpenAIDenseEmbedding.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; require_once __DIR__ . '/EmbeddingInterfaces.php'; diff --git a/src/embeddings/QwenDenseEmbedding.php b/src/embeddings/QwenDenseEmbedding.php index 89f3250..af95ed9 100644 --- a/src/embeddings/QwenDenseEmbedding.php +++ b/src/embeddings/QwenDenseEmbedding.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace CrazyGoat\ZVec; + if (extension_loaded('zvec')) return; require_once __DIR__ . '/EmbeddingInterfaces.php'; diff --git a/tests/test_embeddings_integration.phpt b/tests/test_embeddings_integration.phpt index c331cf3..355dfca 100644 --- a/tests/test_embeddings_integration.phpt +++ b/tests/test_embeddings_integration.phpt @@ -1,11 +1,15 @@ --TEST-- Extensions: Embedding Functions - Integration with ZVec --SKIPIF-- - + --FILE-- + --FILE-- implementsInterface('DenseEmbeddingFunction')) { + $reflection = new ReflectionClass(OpenAIDenseEmbedding::class); + if ($reflection->implementsInterface(DenseEmbeddingFunction::class)) { echo "PASS: OpenAIDenseEmbedding implements DenseEmbeddingFunction\n"; } else { echo "FAIL: OpenAIDenseEmbedding does not implement DenseEmbeddingFunction\n"; exit(1); } - if ($reflection->isSubclassOf('ApiEmbeddingFunction')) { + if ($reflection->isSubclassOf(ApiEmbeddingFunction::class)) { echo "PASS: OpenAIDenseEmbedding extends ApiEmbeddingFunction\n"; } else { echo "FAIL: OpenAIDenseEmbedding does not extend ApiEmbeddingFunction\n"; @@ -49,18 +55,18 @@ if (class_exists('OpenAIDenseEmbedding')) { } // Test 3: Verify QwenDenseEmbedding class exists and implements interface -if (class_exists('QwenDenseEmbedding')) { +if (class_exists(QwenDenseEmbedding::class)) { echo "PASS: QwenDenseEmbedding class exists\n"; - $reflection = new ReflectionClass('QwenDenseEmbedding'); - if ($reflection->implementsInterface('DenseEmbeddingFunction')) { + $reflection = new ReflectionClass(QwenDenseEmbedding::class); + if ($reflection->implementsInterface(DenseEmbeddingFunction::class)) { echo "PASS: QwenDenseEmbedding implements DenseEmbeddingFunction\n"; } else { echo "FAIL: QwenDenseEmbedding does not implement DenseEmbeddingFunction\n"; exit(1); } - if ($reflection->isSubclassOf('ApiEmbeddingFunction')) { + if ($reflection->isSubclassOf(ApiEmbeddingFunction::class)) { echo "PASS: QwenDenseEmbedding extends ApiEmbeddingFunction\n"; } else { echo "FAIL: QwenDenseEmbedding does not extend ApiEmbeddingFunction\n"; @@ -72,7 +78,7 @@ if (class_exists('QwenDenseEmbedding')) { } // Test 4: Verify constants -$openaiReflection = new ReflectionClass('OpenAIDenseEmbedding'); +$openaiReflection = new ReflectionClass(OpenAIDenseEmbedding::class); $constants = $openaiReflection->getConstants(); $expectedConstants = ['MODEL_SMALL', 'MODEL_LARGE', 'MODEL_ADA']; @@ -85,7 +91,7 @@ foreach ($expectedConstants as $const) { } } -$qwenReflection = new ReflectionClass('QwenDenseEmbedding'); +$qwenReflection = new ReflectionClass(QwenDenseEmbedding::class); $constants = $qwenReflection->getConstants(); $expectedConstants = ['MODEL_V4', 'MODEL_V3', 'MODEL_V2', 'MODEL_V1']; diff --git a/tests/test_namespace_bc.phpt b/tests/test_namespace_bc.phpt new file mode 100644 index 0000000..af13985 --- /dev/null +++ b/tests/test_namespace_bc.phpt @@ -0,0 +1,105 @@ +--TEST-- +Namespace: PSR-4 namespace with backward-compatible global aliases +--SKIPIF-- + +--FILE-- + +--EXPECT-- +--- Global namespace (BC aliases) --- +ZVec: OK +ZVecException: OK +ZVecSchema: OK +ZVecDoc: OK +ZVecCollectionOptions: OK +ZVecCollectionStats: OK +ZVecFieldSchema: OK +ZVecIndexParams: OK +ZVecQueryInterface: OK +ZVecVectorQuery: OK +ZVecGroupByVectorQuery: OK +ZVecReRanker: OK +ZVecRerankedDoc: OK +ZVecRrfReRanker: OK +ZVecWeightedReRanker: OK + +--- PSR-4 namespace --- +CrazyGoat\ZVec\ZVec: OK +CrazyGoat\ZVec\ZVecException: OK +CrazyGoat\ZVec\ZVecSchema: OK +CrazyGoat\ZVec\ZVecDoc: OK +CrazyGoat\ZVec\ZVecCollectionOptions: OK +CrazyGoat\ZVec\ZVecCollectionStats: OK +CrazyGoat\ZVec\ZVecFieldSchema: OK +CrazyGoat\ZVec\ZVecIndexParams: OK +CrazyGoat\ZVec\ZVecQueryInterface: OK +CrazyGoat\ZVec\ZVecVectorQuery: OK +CrazyGoat\ZVec\ZVecGroupByVectorQuery: OK +CrazyGoat\ZVec\ZVecReRanker: OK +CrazyGoat\ZVec\ZVecRerankedDoc: OK +CrazyGoat\ZVec\ZVecRrfReRanker: OK +CrazyGoat\ZVec\ZVecWeightedReRanker: OK + +--- Alias instanceof check --- +Global ZVecSchema instanceof: OK +Namespaced ZVecSchema instanceof: OK + +--- Namespace usage with use statement --- +use ZVec: OK +use ZVecSchema: OK +use ZVecDoc: OK + +All namespace tests passed!