- Implement Android platform class with API level detection and compiler configuration - Add Android-specific library linking with proper archive grouping and system libraries - Integrate Android target detection and SDK directory resolution in build configuration - Add Android-specific compile flags including static libstdc++ and security hardening - Include Android platform support in translator and compiler base classes - Add PHPX_ANDROID=1 define flag for Android builds - Supportmaster
parent
f3e2e32648
commit
8dfb363422
8 changed files with 316 additions and 2 deletions
@ -0,0 +1,23 @@ |
||||
<?php |
||||
|
||||
use PHPUnit\Framework\TestCase; |
||||
use TypePhp\Type; |
||||
|
||||
final class FixedTypeDefaultValueTest extends TestCase |
||||
{ |
||||
public function testFixedValueTypesExposeTheirInitialStateExpressions(): void |
||||
{ |
||||
self::assertSame('0', Type::getDefaultValueExpression(Type::INT)); |
||||
self::assertSame('0.0', Type::getDefaultValueExpression(Type::FLOAT)); |
||||
self::assertSame('false', Type::getDefaultValueExpression(Type::BOOL)); |
||||
self::assertSame('php::Str()', Type::getDefaultValueExpression(Type::STR)); |
||||
self::assertSame('php::Array{}', Type::getDefaultValueExpression(Type::ARRAY)); |
||||
} |
||||
|
||||
public function testObjectAndDynamicStorageHaveSeparateEmptyStateRules(): void |
||||
{ |
||||
self::assertNull(Type::getDefaultValueExpression(Type::OBJECT)); |
||||
self::assertNull(Type::getDefaultValueExpression(Type::VAR)); |
||||
self::assertNull(Type::getDefaultValueExpression(Type::REF)); |
||||
} |
||||
} |
||||
@ -0,0 +1,134 @@ |
||||
<?php |
||||
|
||||
namespace TypePhp\Platform; |
||||
|
||||
/** Android arm64-v8a cross-compilation target. */ |
||||
final class Android extends UnixPlatform |
||||
{ |
||||
private const DEFAULT_API = 24; |
||||
|
||||
public static function supportsTarget(string $target): bool |
||||
{ |
||||
return preg_match('/^aarch64-linux-android(?:[0-9]+)?$/i', $target) === 1; |
||||
} |
||||
|
||||
public static function getApiLevel(string $target = ''): int |
||||
{ |
||||
if (preg_match('/^aarch64-linux-android([0-9]+)$/i', $target, $matches) === 1) { |
||||
return (int) $matches[1]; |
||||
} |
||||
|
||||
$configured = getenv('TYPEPHP_ANDROID_API'); |
||||
return is_string($configured) && preg_match('/^[0-9]+$/', $configured) === 1 |
||||
? (int) $configured |
||||
: self::DEFAULT_API; |
||||
} |
||||
|
||||
public function getName(): string |
||||
{ |
||||
return 'Android arm64-v8a'; |
||||
} |
||||
|
||||
public function isCurrent(): bool |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function getSharedLibraryExtension(): string |
||||
{ |
||||
return '.so'; |
||||
} |
||||
|
||||
public function getSharedLinkFlag(): string |
||||
{ |
||||
return '-shared -static-libstdc++' |
||||
. ' -Wl,-z,max-page-size=16384,-z,common-page-size=16384' |
||||
. ' -Wl,-z,relro,-z,now'; |
||||
} |
||||
|
||||
public function getDefaultCompiler(): string |
||||
{ |
||||
$ndkRoot = getenv('ANDROID_NDK_HOME') ?: getenv('ANDROID_NDK_ROOT'); |
||||
if (!is_string($ndkRoot) || $ndkRoot === '') { |
||||
throw new \RuntimeException('Set ANDROID_NDK_HOME to an Android NDK installation'); |
||||
} |
||||
|
||||
$hostTag = match (PHP_OS_FAMILY) { |
||||
'Darwin' => 'darwin-x86_64', |
||||
'Windows' => 'windows-x86_64', |
||||
default => 'linux-x86_64', |
||||
}; |
||||
$compiler = rtrim($ndkRoot, '/\\') |
||||
. '/toolchains/llvm/prebuilt/' . $hostTag . '/bin/clang++'; |
||||
if (!is_file($compiler) || !is_executable($compiler)) { |
||||
throw new \RuntimeException("Android NDK clang++ was not found: {$compiler}"); |
||||
} |
||||
return $compiler; |
||||
} |
||||
|
||||
public function getDefaultRpaths(?string $phpxDir = null, ?string $phpDir = null): array |
||||
{ |
||||
return []; |
||||
} |
||||
|
||||
public function buildPhpIncludePaths(string $phpDir): array |
||||
{ |
||||
$paths = [ |
||||
$phpDir . '/include/php', |
||||
$phpDir . '/include/php/main', |
||||
$phpDir . '/include/php/TSRM', |
||||
$phpDir . '/include/php/Zend', |
||||
$phpDir . '/include/php/ext', |
||||
$phpDir . '/include/php/ext/date/lib', |
||||
]; |
||||
return array_values(array_filter($paths, is_dir(...))); |
||||
} |
||||
|
||||
public function buildPhpLibPaths(string $phpDir): array |
||||
{ |
||||
$libDir = rtrim($phpDir, '/') . '/lib'; |
||||
return is_dir($libDir) ? [$libDir] : []; |
||||
} |
||||
|
||||
public function detectPhpLibs(string $phpDir): array |
||||
{ |
||||
$archive = rtrim($phpDir, '/') . '/lib/libphp.a'; |
||||
if (!is_file($archive)) { |
||||
throw new \RuntimeException("The Android libphp.a was not found: {$archive}"); |
||||
} |
||||
return ['embed' => null, 'static' => $archive, 'is_shared' => false]; |
||||
} |
||||
|
||||
/** Keep the mutually dependent PHP/PHPX archives in one linker group. */ |
||||
public function getLibraryFlags(array $libraries): string |
||||
{ |
||||
$archives = []; |
||||
$systemLibraries = []; |
||||
foreach ($libraries as $library) { |
||||
if (is_string($library) && str_ends_with($library, '.a') && is_file($library)) { |
||||
$archives[] = escapeshellarg($library); |
||||
} else { |
||||
$name = basename((string) $library); |
||||
if (str_starts_with($name, 'lib')) { |
||||
$name = substr($name, 3); |
||||
} |
||||
$systemLibraries[] = '-l' . preg_replace('/\.(?:a|so)$/', '', $name); |
||||
} |
||||
} |
||||
|
||||
$flags = []; |
||||
if ($archives !== []) { |
||||
$flags[] = '-Wl,--start-group ' . implode(' ', $archives) . ' -Wl,--end-group'; |
||||
} |
||||
return implode(' ', array_merge($flags, $systemLibraries)); |
||||
} |
||||
|
||||
public function getBuildLibraryWarnings( |
||||
string $phpDir, |
||||
string $phpxDir, |
||||
string $buildMode, |
||||
bool $checkPhpxRuntime = true, |
||||
): array { |
||||
return []; |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@ |
||||
--TEST-- |
||||
unset restores fixed typed variables to their initial states |
||||
--FILE-- |
||||
<?php |
||||
class UnsetFixedTypeObject |
||||
{ |
||||
} |
||||
|
||||
function makeUnsetArrayValue(): int |
||||
{ |
||||
return 7; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$integer = 42; |
||||
$float = 2.5; |
||||
$boolean = true; |
||||
$string = 'value'; |
||||
$array = [1, 2]; |
||||
$object = new UnsetFixedTypeObject(); |
||||
$dynamic = std::any('value'); |
||||
|
||||
unset($integer, $float, $boolean, $string, $array, $object, $dynamic); |
||||
|
||||
var_dump($integer, $float, $boolean, $string, $array, $object); |
||||
var_dump( |
||||
isset($integer), |
||||
isset($float), |
||||
isset($boolean), |
||||
isset($string), |
||||
isset($array), |
||||
isset($object), |
||||
isset($dynamic), |
||||
); |
||||
|
||||
$array[] = 5; |
||||
unset($array); |
||||
$appendResult = ($array[] = makeUnsetArrayValue()); |
||||
var_dump($array, $appendResult); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(0) |
||||
float(0) |
||||
bool(false) |
||||
string(0) "" |
||||
array(0) { |
||||
} |
||||
NULL |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(false) |
||||
bool(false) |
||||
array(1) { |
||||
[0]=> |
||||
int(7) |
||||
} |
||||
int(7) |
||||
Loading…
Reference in new issue