- 添加匿名类在命名空间中的测试 - 添加跨命名空间类常量引用的测试 - 添加跨命名空间继承的测试 - 添加完全限定名与相对名的测试 - 添加组合use导入的测试 - 添加跨命名空间接口实现的测试 - 添加混合use类和函数导入的测试 - 添加命名空间常量定义的测试 - 添加多层嵌套命名空间的测试 - 添加self/static跨命名空间解析的测试 - 添加跨命名空间trait使用的测试 - 添加use别名的测试 - 添加use const导入常量的测试pull/1/head
parent
928f9050d6
commit
192bba3454
13 changed files with 646 additions and 0 deletions
@ -0,0 +1,35 @@ |
||||
--TEST-- |
||||
Anonymous class inside namespace |
||||
--FILE-- |
||||
<?php |
||||
namespace App\Factory { |
||||
interface Greeter { |
||||
public function greet(string $name): string; |
||||
} |
||||
|
||||
function createGreeter(string $prefix): Greeter { |
||||
return new class($prefix) implements Greeter { |
||||
private string $prefix; |
||||
public function __construct(string $prefix) { |
||||
$this->prefix = $prefix; |
||||
} |
||||
public function greet(string $name): string { |
||||
return $this->prefix . " " . $name; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use function App\Factory\createGreeter; |
||||
|
||||
function main() { |
||||
$g = createGreeter("Hello"); |
||||
var_dump($g->greet("World")); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(11) "Hello World" |
||||
done |
||||
@ -0,0 +1,61 @@ |
||||
--TEST-- |
||||
Class constant references across namespaces |
||||
--FILE-- |
||||
<?php |
||||
namespace Core\Status { |
||||
class HttpStatus { |
||||
public const OK = 200; |
||||
public const NOT_FOUND = 404; |
||||
public const SERVER_ERROR = 500; |
||||
} |
||||
|
||||
class AppConfig { |
||||
public const MAX_CONNECTIONS = 100; |
||||
public const TIMEOUT = 30; |
||||
} |
||||
} |
||||
|
||||
namespace Services { |
||||
use Core\Status\HttpStatus; |
||||
use Core\Status\AppConfig; |
||||
|
||||
class ApiClient { |
||||
public static function check(int $code): string { |
||||
switch ($code) { |
||||
case HttpStatus::OK: |
||||
return "success"; |
||||
case HttpStatus::NOT_FOUND: |
||||
return "not found"; |
||||
case HttpStatus::SERVER_ERROR: |
||||
return "error"; |
||||
default: |
||||
return "unknown"; |
||||
} |
||||
} |
||||
|
||||
public static function getMaxConnections(): int { |
||||
return AppConfig::MAX_CONNECTIONS; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use Services\ApiClient; |
||||
|
||||
function main() { |
||||
var_dump(ApiClient::check(200)); |
||||
var_dump(ApiClient::check(404)); |
||||
var_dump(ApiClient::check(500)); |
||||
var_dump(ApiClient::getMaxConnections()); |
||||
var_dump(\Core\Status\AppConfig::TIMEOUT); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(7) "success" |
||||
string(9) "not found" |
||||
string(5) "error" |
||||
int(100) |
||||
int(30) |
||||
done |
||||
@ -0,0 +1,80 @@ |
||||
--TEST-- |
||||
Namespace with class inheritance across namespaces |
||||
--FILE-- |
||||
<?php |
||||
namespace Base\Entity { |
||||
abstract class Model { |
||||
protected string $table; |
||||
|
||||
public function __construct(string $table) { |
||||
$this->table = $table; |
||||
} |
||||
|
||||
public function getTable(): string { |
||||
return $this->table; |
||||
} |
||||
|
||||
abstract public function fields(): array; |
||||
} |
||||
} |
||||
|
||||
namespace App\Models { |
||||
use Base\Entity\Model; |
||||
|
||||
class User extends Model { |
||||
public function __construct() { |
||||
parent::__construct("users"); |
||||
} |
||||
|
||||
public function fields(): array { |
||||
return ["id", "name", "email"]; |
||||
} |
||||
} |
||||
|
||||
class Product extends Model { |
||||
public function __construct() { |
||||
parent::__construct("products"); |
||||
} |
||||
|
||||
public function fields(): array { |
||||
return ["id", "title", "price"]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use App\Models\User; |
||||
use App\Models\Product; |
||||
|
||||
function main() { |
||||
$user = new User(); |
||||
$product = new Product(); |
||||
|
||||
var_dump($user->getTable()); |
||||
var_dump($user->fields()); |
||||
var_dump($product->getTable()); |
||||
var_dump($product->fields()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(5) "users" |
||||
array(3) { |
||||
[0]=> |
||||
string(2) "id" |
||||
[1]=> |
||||
string(4) "name" |
||||
[2]=> |
||||
string(5) "email" |
||||
} |
||||
string(8) "products" |
||||
array(3) { |
||||
[0]=> |
||||
string(2) "id" |
||||
[1]=> |
||||
string(5) "title" |
||||
[2]=> |
||||
string(5) "price" |
||||
} |
||||
done |
||||
@ -0,0 +1,41 @@ |
||||
--TEST-- |
||||
Fully qualified names vs relative names in namespace |
||||
--FILE-- |
||||
<?php |
||||
namespace Foo\Bar { |
||||
class Helper { |
||||
public static function version(): string { |
||||
return "1.0.0"; |
||||
} |
||||
} |
||||
|
||||
function baz(): string { |
||||
return "baz-in-foo-bar"; |
||||
} |
||||
} |
||||
|
||||
namespace Foo { |
||||
class Runner { |
||||
// Relative: Bar\Helper resolves to Foo\Bar\Helper |
||||
public static function run(): string { |
||||
return Bar\Helper::version(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main() { |
||||
// Fully qualified calls |
||||
var_dump(\Foo\Bar\Helper::version()); |
||||
var_dump(\Foo\Bar\baz()); |
||||
// Call into Foo namespace class |
||||
var_dump(\Foo\Runner::run()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(5) "1.0.0" |
||||
string(14) "baz-in-foo-bar" |
||||
string(5) "1.0.0" |
||||
done |
||||
@ -0,0 +1,46 @@ |
||||
--TEST-- |
||||
Group use imports for classes, functions, and constants |
||||
--FILE-- |
||||
<?php |
||||
namespace Vendor\Package { |
||||
class ClassA { |
||||
public static function name(): string { return "ClassA"; } |
||||
} |
||||
class ClassB { |
||||
public static function name(): string { return "ClassB"; } |
||||
} |
||||
class ClassC { |
||||
public static function name(): string { return "ClassC"; } |
||||
} |
||||
function funcA(): string { return "funcA"; } |
||||
function funcB(): string { return "funcB"; } |
||||
const CONST_A = "constA"; |
||||
const CONST_B = "constB"; |
||||
} |
||||
|
||||
namespace { |
||||
use Vendor\Package\{ClassA, ClassB, ClassC}; |
||||
use function Vendor\Package\{funcA, funcB}; |
||||
use const Vendor\Package\{CONST_A, CONST_B}; |
||||
|
||||
function main() { |
||||
var_dump(ClassA::name()); |
||||
var_dump(ClassB::name()); |
||||
var_dump(ClassC::name()); |
||||
var_dump(funcA()); |
||||
var_dump(funcB()); |
||||
var_dump(CONST_A); |
||||
var_dump(CONST_B); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(6) "ClassA" |
||||
string(6) "ClassB" |
||||
string(6) "ClassC" |
||||
string(5) "funcA" |
||||
string(5) "funcB" |
||||
string(6) "constA" |
||||
string(6) "constB" |
||||
done |
||||
@ -0,0 +1,58 @@ |
||||
--TEST-- |
||||
Namespace with interface and implementation across namespaces |
||||
--FILE-- |
||||
<?php |
||||
namespace Contracts\Payment { |
||||
interface PaymentProcessor { |
||||
public function pay(float $amount): bool; |
||||
public function refund(float $amount): bool; |
||||
} |
||||
} |
||||
|
||||
namespace Services\Payment { |
||||
use Contracts\Payment\PaymentProcessor; |
||||
|
||||
class StripeProcessor implements PaymentProcessor { |
||||
private array $log = []; |
||||
|
||||
public function pay(float $amount): bool { |
||||
$this->log[] = "paid:{$amount}"; |
||||
return true; |
||||
} |
||||
|
||||
public function refund(float $amount): bool { |
||||
$this->log[] = "refunded:{$amount}"; |
||||
return true; |
||||
} |
||||
|
||||
public function getLog(): array { |
||||
return $this->log; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use Services\Payment\StripeProcessor; |
||||
use Contracts\Payment\PaymentProcessor; |
||||
|
||||
function main() { |
||||
$p = new StripeProcessor(); |
||||
var_dump($p->pay(100.0)); |
||||
var_dump($p->refund(50.0)); |
||||
var_dump($p->getLog()); |
||||
var_dump($p instanceof PaymentProcessor); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(true) |
||||
array(2) { |
||||
[0]=> |
||||
string(8) "paid:100" |
||||
[1]=> |
||||
string(11) "refunded:50" |
||||
} |
||||
bool(true) |
||||
done |
||||
@ -0,0 +1,51 @@ |
||||
--TEST-- |
||||
Mixed use class and use function imports |
||||
--FILE-- |
||||
<?php |
||||
namespace Utils\Str { |
||||
function slug(string $s): string { |
||||
return str_replace(" ", "-", strtolower($s)); |
||||
} |
||||
|
||||
function prefix(string $s, string $p): string { |
||||
return "{$p}:{$s}"; |
||||
} |
||||
} |
||||
|
||||
namespace Utils\Math { |
||||
function double(int $x): int { |
||||
return $x * 2; |
||||
} |
||||
} |
||||
|
||||
namespace App { |
||||
use Utils\Str\slug; |
||||
use Utils\Str\prefix; |
||||
use function Utils\Math\double; |
||||
|
||||
class TextHelper { |
||||
public static function normalize(string $text): string { |
||||
return slug(prefix($text, "msg")); |
||||
} |
||||
} |
||||
|
||||
function compute(int $v): int { |
||||
return double($v); |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use App\TextHelper; |
||||
use function App\compute; |
||||
|
||||
function main() { |
||||
var_dump(TextHelper::normalize("Hello World")); |
||||
var_dump(compute(21)); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(15) "msg:hello-world" |
||||
int(42) |
||||
done |
||||
@ -0,0 +1,43 @@ |
||||
--TEST-- |
||||
Namespace with constants defined via const keyword |
||||
--FILE-- |
||||
<?php |
||||
namespace Config\App { |
||||
const APP_NAME = "MyApp"; |
||||
const VERSION = "2.0.0"; |
||||
const DEBUG = true; |
||||
|
||||
function getConfig(): array { |
||||
return [ |
||||
"name" => APP_NAME, |
||||
"version" => VERSION, |
||||
"debug" => DEBUG, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use function Config\App\getConfig; |
||||
|
||||
function main() { |
||||
var_dump(\Config\App\APP_NAME); |
||||
var_dump(\Config\App\VERSION); |
||||
var_dump(\Config\App\DEBUG); |
||||
var_dump(getConfig()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(5) "MyApp" |
||||
string(5) "2.0.0" |
||||
bool(true) |
||||
array(3) { |
||||
["name"]=> |
||||
string(5) "MyApp" |
||||
["version"]=> |
||||
string(5) "2.0.0" |
||||
["debug"]=> |
||||
bool(true) |
||||
} |
||||
done |
||||
@ -0,0 +1,38 @@ |
||||
--TEST-- |
||||
Multi-level nested namespaces with classes and functions |
||||
--FILE-- |
||||
<?php |
||||
namespace App\Services\Payment { |
||||
class Gateway { |
||||
public static function process(string $type): string { |
||||
return "processed:{$type}"; |
||||
} |
||||
} |
||||
|
||||
function fee(int $amount): int { |
||||
return (int)($amount * 5 / 100); |
||||
} |
||||
} |
||||
|
||||
namespace App\Controllers { |
||||
class OrderController { |
||||
public static function create(): string { |
||||
return \App\Services\Payment\Gateway::process("order"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main() { |
||||
var_dump(\App\Services\Payment\Gateway::process("sale")); |
||||
var_dump(\App\Services\Payment\fee(100)); |
||||
var_dump(\App\Controllers\OrderController::create()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(14) "processed:sale" |
||||
int(5) |
||||
string(15) "processed:order" |
||||
done |
||||
@ -0,0 +1,52 @@ |
||||
--TEST-- |
||||
self/static resolution with inheritance across namespaces |
||||
--FILE-- |
||||
<?php |
||||
namespace Base\Core { |
||||
class BaseController { |
||||
protected static string $prefix = "base"; |
||||
|
||||
public static function whoAmI(): string { |
||||
return static::$prefix; |
||||
} |
||||
|
||||
public static function selfWhoAmI(): string { |
||||
return self::$prefix; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace App\Http { |
||||
use Base\Core\BaseController; |
||||
|
||||
class UserController extends BaseController { |
||||
protected static string $prefix = "user"; |
||||
} |
||||
|
||||
class AdminController extends BaseController { |
||||
protected static string $prefix = "admin"; |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use App\Http\UserController; |
||||
use App\Http\AdminController; |
||||
use Base\Core\BaseController; |
||||
|
||||
function main() { |
||||
// static:: should resolve to the calling class |
||||
var_dump(UserController::whoAmI()); |
||||
var_dump(AdminController::whoAmI()); |
||||
// self:: should always resolve to BaseController |
||||
var_dump(BaseController::selfWhoAmI()); |
||||
var_dump(UserController::selfWhoAmI()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(4) "user" |
||||
string(5) "admin" |
||||
string(4) "base" |
||||
string(4) "base" |
||||
done |
||||
@ -0,0 +1,67 @@ |
||||
--TEST-- |
||||
Trait used across namespaces |
||||
--FILE-- |
||||
<?php |
||||
namespace Base\Traits { |
||||
trait Logger { |
||||
private array $log = []; |
||||
|
||||
public function log(string $msg): void { |
||||
$this->log[] = $msg; |
||||
} |
||||
|
||||
public function getLog(): array { |
||||
return $this->log; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace App\Services { |
||||
use Base\Traits\Logger; |
||||
|
||||
class OrderService { |
||||
use Logger; |
||||
|
||||
public function createOrder(int $id): string { |
||||
$this->log("order:{$id}"); |
||||
return "ok"; |
||||
} |
||||
} |
||||
|
||||
class UserService { |
||||
use Logger; |
||||
|
||||
public function createUser(string $name): string { |
||||
$this->log("user:{$name}"); |
||||
return "ok"; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use App\Services\OrderService; |
||||
use App\Services\UserService; |
||||
|
||||
function main() { |
||||
$os = new OrderService(); |
||||
$us = new UserService(); |
||||
|
||||
$os->createOrder(42); |
||||
$us->createUser("alice"); |
||||
|
||||
var_dump($os->getLog()); |
||||
var_dump($us->getLog()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(1) { |
||||
[0]=> |
||||
string(8) "order:42" |
||||
} |
||||
array(1) { |
||||
[0]=> |
||||
string(10) "user:alice" |
||||
} |
||||
done |
||||
@ -0,0 +1,35 @@ |
||||
--TEST-- |
||||
use alias for class names |
||||
--FILE-- |
||||
<?php |
||||
namespace Library\Storage\Drivers { |
||||
class FileDriver { |
||||
public function read(): string { |
||||
return "file:data"; |
||||
} |
||||
} |
||||
|
||||
class MemoryDriver { |
||||
public function read(): string { |
||||
return "memory:data"; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use Library\Storage\Drivers\FileDriver as FS; |
||||
use Library\Storage\Drivers\MemoryDriver as Mem; |
||||
|
||||
function main() { |
||||
$fs = new FS(); |
||||
$mem = new Mem(); |
||||
var_dump($fs->read()); |
||||
var_dump($mem->read()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(9) "file:data" |
||||
string(11) "memory:data" |
||||
done |
||||
@ -0,0 +1,39 @@ |
||||
--TEST-- |
||||
use const for importing namespace constants |
||||
--FILE-- |
||||
<?php |
||||
namespace Lib\Config { |
||||
const HOST = "localhost"; |
||||
const PORT = 8080; |
||||
const DEBUG = true; |
||||
} |
||||
|
||||
namespace App { |
||||
use const Lib\Config\HOST; |
||||
use const Lib\Config\PORT; |
||||
use const Lib\Config\DEBUG; |
||||
|
||||
function getServer(): string { |
||||
return HOST . ":" . PORT; |
||||
} |
||||
|
||||
function isDebug(): bool { |
||||
return DEBUG; |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
use function App\getServer; |
||||
use function App\isDebug; |
||||
|
||||
function main() { |
||||
var_dump(getServer()); |
||||
var_dump(isDebug()); |
||||
echo "done\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(14) "localhost:8080" |
||||
bool(true) |
||||
done |
||||
Loading…
Reference in new issue