Crazy Fun Experiments with PHP�(Not for Production)
�SymfonyCon Amsterdam 2019
@ZanBaldwin
https://bit.ly/2QF3zh0
About This Talk
About This Talk
what?
huh?
About This Talk
what?
huh?
I wonder...
what if?
About This Talk
what?
huh?
I wonder...
what if?
About This Talk
what?
huh?
I wonder...
what if?
About This Talk
what?
huh?
I wonder...
what if?
About This Talk
what?
huh?
I wonder...
what if?
PERFORMANCE
Streams
Computer Science
A sequence of data elements made available over time.
Can have finite size, or be continuous.
Read from & written to in chunks.
Computer Science
A sequence of data elements made available over time.
Can have finite size, or be continuous.
Read from & written to in chunks.
PHP Streams
A way of generalizing file, network and other operations which share a common set of functions.
A stream is a resource object that can be read from or written to in a linear fashion.
You’ve Used Streams
file_get_contents(‘ hello.txt’);
file_get_contents(‘file://hello.txt’);
Supported Protocols (Stream Wrappers)
Native PHP
Supported Protocols (Stream Wrappers)
Native PHP
Enabled through PHP extensions
s3://bucket/key
Esoteric Language
Brainf*ck
Esoteric Language
Brainf*ck
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
hello.bf
require ‘ /app/hello.bf’;
require ‘file:///app/hello.bf’;
require ‘ bf:///app/hello.bf’;
class BfStreamWrapper
{
/** @var resource $context */
public $context;
public static function register(string $filterName): void
{
stream_wrapper_register($filterName, static::class);
}
public function stream_open(string $path, string $mode, int $options, &$opened_path): bool;�� public function stream_stat(): array;�� public function stream_read(int $readNumBytes);�� public function stream_eof(): bool;�� public function stream_close(): bool;���
}
class BfStreamWrapper
{
// ...
private $input = [];
private $output;
private function execute(): void
{
if (is_string($output)) {
return;
}
$result = (new \Brainfuck\Language)->run(
file_get_contents('file://' . $this->uri),
$this->input
);
$this->output = implode('', array_map(function (int $ord): string {
// Brainf*ck returns result in bytes (8-bit integers). Convert to ASCII.
return chr($ord);
}, $result));
}
}
Input?
class BfStreamWrapper
{
/** @var resource $context */
public $context;
public static function register(string $filterName): void
{
stream_wrapper_register($filterName, static::class);
}
public function stream_open(string $path, string $mode, int $options, &$opened_path): bool;�� public function stream_stat(): array;�� public function stream_read(int $readNumBytes);�� public function stream_eof(): bool;�� public function stream_close(): bool;�
public function stream_write(string $data): int;�
}
index.php
BfStreamWrapper::register('bf');
$stream = 'bf:///app/hello.bf';
$handle = fopen($stream, ‘r+’);
fwrite($handle, 'script input');
$output = stream_get_contents($handle);
Stream Filters
php_stream_filter Implementation
class BfStreamFilter extends \php_user_filter
{
public static function register(string $filterName): void
{
stream_filter_register($filterName, self::class);
}
// ...
}
php_stream_filter Implementation
public function filter($in, $out, &$consumed, $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$this->input .= $bucket->data;
}
if ($closing || feof($this->stream)) {
$consumed = strlen($this->input);
$bucket = stream_bucket_new(
$this->stream,
// Has to be static because userland doesn't control instantiation.
static::$twig->render('bf_closure.twig.php', [
'bf_php_value' => var_export($this->input, true),
])
);
stream_bucket_append($out, $bucket);
return \PSFS_PASS_ON;
}
return \PSFS_FEED_ME;
}
php_stream_filter Implementation
public function filter($in, $out, &$consumed, $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$this->input .= $bucket->data;
}
if ($closing || feof($this->stream)) {
$consumed = strlen($this->input);
$bucket = stream_bucket_new(
$this->stream,
// Has to be static because userland doesn't control instantiation.
static::$twig->render('bf_closure.twig.php', [
'bf_php_value' => var_export($this->input, true),
])
);
stream_bucket_append($out, $bucket);
return \PSFS_PASS_ON;
}
return \PSFS_FEED_ME;
}
bf_closure.twig.php
<?php declare(strict_types=1);
return function (array $input = []): string {
$result = (new \Brainfuck\Language)->run(
,
$input
);
return implode('', array_map(
function (int $ord): string {
// BF returns result in bytes (8-bit
// integers). Convert to ASCII.
return chr($ord);
},
$result
));
};
{% Brainf*ck Closure Code Template %}
��
{{ bf_php_value }}
index.php
BfStreamFilter::register('bf');
$handle = fopen('/app/hello.bf', 'r+');
stream_filter_append($handle, 'bf');
$closureCode = stream_get_contents($handle);
$closure = eval('?>' . $closureCode);
$output = $closure($input);
php://filter
php://filter
/resource=<resource-name>
The resource identifier of the stream you’d like to filter.
php://filter
/resource=<resource-name>���/read=<list,of,filter,names>
The resource identifier of the stream you’d like to filter.��Apply the specified filters when reading data from the stream.
php://filter
/resource=<resource-name>���/read=<list,of,filter,names>���/write=<list,of,filter,names>
The resource identifier of the stream you’d like to filter.��Apply the specified filters when reading data from the stream.��Apply the specified filters when writing data to the stream.
php://filter
BfStreamFilter::register('bf');
$script = '/app/hello.bf';
# php://filter/read=bf/resource=file:///app/hello.bf
$closure = require 'php://filter'
. '/read=bf'
. '/resource=file://' . $script;
$output = $closure($input);
Go! AOP�by Alexander Lisachenko
Aspect-oriented Programming
Aspect-oriented Programming
PRODUCTION READY! 🎉
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
}
function createNewUser(string $email, string $password): UserInterface {����
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();������
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}�
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();������
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}�
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();�����
$this->logger->info(‘User created successfully.’, [‘email’ => $email]);
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}�
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();����
$this->eventDispatcher->dispatch(new UserCreatedEvent($email));
$this->logger->info(‘User created successfully.’, [‘email’ => $email]);
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}
try {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
} catch (ORMException $e) {
$this->logger->error(‘Could not persist new user.’, [‘email’ => $email]);
throw $e;
}
$this->eventDispatcher->dispatch(new UserCreatedEvent($email));
$this->logger->info(‘User created successfully.’, [‘email’ => $email]);
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}
try {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
} catch (ORMException $e) {
$this->logger->error(‘Could not persist new user.’, [‘email’ => $email]);
throw $e;
}
$this->eventDispatcher->dispatch(new UserCreatedEvent($email));
$this->logger->info(‘User created successfully.’, [‘email’ => $email]);
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user
}
/** @Before(pointcut=”public UserService->*(*)”) */
function checkCurrentUserCanCreateUsers(MethodInvocation $method): void {
if (!$this->security->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException;
}
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user
}
/** @Before(pointcut=”public UserService->*(*)”) */
function checkCurrentUserCanCreateUsers(MethodInvocation $method): void {
if (!$this->security->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException;
}
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user
}
/** @Before(pointcut=”public UserService->*(*)”) */
function checkCurrentUserCanCreateUsers(MethodInvocation $method): void {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}
}
/** @AfterThrow(pointcut=”public UserService->createNewUser(*)”) */
function handleNewUserDatabaseError(MethodInvocation $method): void {
$this->logger->error('Could not persist new user.', ['email' => $method->getArguments()[0]]);
throw $e;
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user
}
/** @Before(pointcut=”public UserService->*(*)”) */
function checkCurrentUserCanCreateUsers(MethodInvocation $method): void {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}
}
/** @AfterThrow(pointcut=”public UserService->createNewUser(*)”) */
function handleNewUserDatabaseError(MethodInvocation $method): void {
$this->logger->error('Could not persist new user.', ['email' => $method->getArguments()[0]]);
throw $e;
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user
}
/** @Before(pointcut=”public UserService->*(*)”) */
function checkCurrentUserCanCreateUsers(MethodInvocation $method): void {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}
}
/** @AfterThrow(pointcut=”public UserService->createNewUser(*)”) */
function handleNewUserDatabaseError(MethodInvocation $method): void {
$this->logger->error(‘Could not persist new user.’, [‘email’ => $method->getArguments()[0]]);
throw $e;
}
/** @After(pointcut=”public UserService->createNewUser(*)”) */
function onNewUserSuccessfullyCreated(MethodInvocation $method): void {
$email = $method->getArguments()[0];
$this->eventDispatcher->dispatch(new UserCreatedEvent($email));
$this->logger->info('User created successfully.', ['email' => $email]);
}
function createNewUser(string $email, string $password): UserInterface {
$user = new User($email, $password);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user
}
/** @Before(pointcut=”public UserService->*(*)”) */
function checkCurrentUserCanCreateUsers(MethodInvocation $method): void {
if (!$this->security->isGranted(‘ROLE_ADMIN’)) {
throw new AccessDeniedException;
}
}
/** @AfterThrow(pointcut=”public UserService->createNewUser(*)”) */
function handleNewUserDatabaseError(MethodInvocation $method): void {
$this->logger->error(‘Could not persist new user.’, [‘email’ => $method->getArguments()[0]]);
throw $e;
}
/** @After(pointcut=”public UserService->createNewUser(*)”) */
function onNewUserSuccessfullyCreated(MethodInvocation $method): void {
$email = $method->getArguments()[0];
$this->eventDispatcher->dispatch(new UserCreatedEvent($email));
$this->logger->info('User created successfully.', ['email' => $email]);
}
/** @Around(pointcut=”public UserService->createNewUser(*)”) */
function logTimeTakenToCreateUser(MethodInvocation $method): UserInterface {
$start = microtime(true);�
$result = $method->proceed();
� $duration = microtime(true) - $start;
$this->logger->log(sprintf('Creating a user took %f seconds', $duration));
� // Modify $result before returning?� return $result;
}
Source Transformers
Go! AOP’s source transforming process:
Go! AOP’s source transforming process:
Go! AOP’s source transforming process:
Go! AOP’s source transforming process:
Go! AOP’s source transforming process:
Go! AOP’s source transforming process:
Automagically ✨
$userService = new UserService(/* dependencies */);
$user = $userService->createNewUser(
'admin@example.com',
'Password'
);
$executableCodeContents =
transformCodeFilter(
$sourceCodeFileOnDisk
);
Autoloader Overloading
new MyClass ⇢ ??? ⇢ transformCodeFilter()
new MyClass;
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
Go! AOP suddenly says “I’d like to interject for a moment…”
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
Go! AOP suddenly says “I’d like to interject for a moment…”
The framework instructs PHP to instead load:�php://filter ↵� /read=go.source.transforming.loader ↵� /resource=file:///var/www/MyClass.php
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
Go! AOP suddenly says “I’d like to interject for a moment…”
The framework instructs PHP to instead load:�php://filter ↵� /read=go.source.transforming.loader ↵� /resource=file:///var/www/MyClass.php
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
Go! AOP suddenly says “I’d like to interject for a moment…”
The framework instructs PHP to instead load:�php://filter ↵� /read=go.source.transforming.loader ↵� /resource=file:///var/www/MyClass.php
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
Go! AOP suddenly says “I’d like to interject for a moment…”
The framework instructs PHP to instead load:�php://filter ↵� /read=go.source.transforming.loader ↵� /resource=file:///var/www/MyClass.php
new MyClass;
Composer determines source file location:�/var/www/MyClass.php
Go! AOP suddenly says “I’d like to interject for a moment…”
The framework instructs PHP to instead load:�php://filter ↵� /read=go.source.transforming.loader ↵� /resource=file:///var/www/MyClass.php
Sneaky! 🐍
spl_autoload_functions();
Go! AOP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
[ Composer\Autoload\ClassLoader, MyAppLoader ]
Go! AOP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
[ Composer\Autoload\ClassLoader, MyAppLoader ]
Hmm, let’s take a closer look at those… 🔍
Go! AOP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
[ Composer\Autoload\ClassLoader, MyAppLoader ]
[ AopComposerLoader(Composer\Autoload\ClassLoader), MyAppLoader ]
Go! AOP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
Hmm, let’s take a closer look at those… 🔍
[ Composer\Autoload\ClassLoader, MyAppLoader ]
[ AopComposerLoader(Composer\Autoload\ClassLoader), MyAppLoader ]
Hot dang! I knew it!
Go! AOP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
Hmm, let’s take a closer look at those… 🔍
[ Composer\Autoload\ClassLoader, MyAppLoader ]
[ AopComposerLoader(Composer\Autoload\ClassLoader), MyAppLoader ]
Okay, thanks PHP! You can have those back now! 😘
Go! AOP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
Hmm, let’s take a closer look at those… 🔍
Hot dang! I knew it!
[ Composer\Autoload\ClassLoader, MyAppLoader ]
[ AopComposerLoader(Composer\Autoload\ClassLoader), MyAppLoader ]
Sure, looks legit 🤷♀️
Go! AOP
PHP
Hey, PHP! Give me all of the registered autoloaders please! 🙏
Hmm, let’s take a closer look at those… 🔍
Hot dang! I knew it!
Okay, thanks PHP! You can have those back now! 😘
The Magic™
class AutoloaderOverloader
{
private $composer;
private $filterName;
protected function __construct(ComposerClassLoader $composer, string $filterName) {
$this->composer = $composer;
$this->filterName = $filterName
}
� ��������
}
The Magic™
class AutoloaderOverloader
{
private $composer;
private $filterName;
protected function __construct(ComposerClassLoader $composer, string $filterName) {
$this->composer = $composer;
$this->filterName = $filterName
}
public function loadClass($class) {
$file = $this->composer->findFile($class);
$compiledFile = $this->cache->findCachedFile($file)
?: 'php://filter/read=' . $this->filterName . '/resource=file://' . $file;
include $compiledFile;
}����
}
The Magic™
class AutoloaderOverloader
{
private $composer;
private $filterName;
protected function __construct(ComposerClassLoader $composer, string $filterName) {
$this->composer = $composer;
$this->filterName = $filterName
}
public function loadClass($class) {
$file = $this->composer->findFile($class);
$compiledFile = $this->cache->findCachedFile($file)
?: 'php://filter/read=' . $this->filterName . '/resource=file://' . $file;
include $compiledFile;
}����
}
No file returned?�Code templates!
The Magic™
class AutoloaderOverloader
{
// ...
public static function init(string $filterName): void
{
$loaders = spl_autoload_functions();
foreach ($loaders as &$loader) {
// Unregister each loader, so they can be re-registered in the same order.
spl_autoload_unregister($loader);
if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
// Replace Composer autoloader with our hijacking autoloader.
$loader[0] = new self($loader[0], $filterName);
}
}
// Processed all the loaders, re-register them with PHP in their original order.
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
}
}
The Magic™
class AutoloaderOverloader
{
// ...
public static function init(string $filterName): void
{
$loaders = spl_autoload_functions();
foreach ($loaders as &$loader) {
// Unregister each loader, so they can be re-registered in the same order.
spl_autoload_unregister($loader);
if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
// Replace Composer autoloader with our hijacking autoloader.
$loader[0] = new self($loader[0], $filterName);
}
}
// Processed all the loaders, re-register them with PHP in their original order.
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
}
}
The Magic™
class AutoloaderOverloader
{
// ...
public static function init(string $filterName): void
{
$loaders = spl_autoload_functions();
foreach ($loaders as &$loader) {
// Unregister each loader, so they can be re-registered in the same order.
spl_autoload_unregister($loader);
if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
// Replace Composer autoloader with our hijacking autoloader.
$loader[0] = new self($loader[0], $filterName);
}
}
// Processed all the loaders, re-register them with PHP in their original order.
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
}
}
The Magic™
class AutoloaderOverloader
{
// ...
public static function init(string $filterName): void
{
$loaders = spl_autoload_functions();
foreach ($loaders as &$loader) {
// Unregister each loader, so they can be re-registered in the same order.
spl_autoload_unregister($loader);
if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
// Replace Composer autoloader with our hijacking autoloader.
$loader[0] = new self($loader[0], $filterName);
}
}
// Processed all the loaders, re-register them with PHP in their original order.
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
}
}
The Magic™
class AutoloaderOverloader
{
// ...
public static function init(string $filterName): void
{
$loaders = spl_autoload_functions();
foreach ($loaders as &$loader) {
// Unregister each loader, so they can be re-registered in the same order.
spl_autoload_unregister($loader);
if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
// Replace Composer autoloader with our hijacking autoloader.
$loader[0] = new self($loader[0], $filterName);
}
}
// Processed all the loaders, re-register them with PHP in their original order.
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
}
}
The Magic™
// index.php
require_once __DIR__ . '/vendor/autoload.php';
stream_filter_register('my_compiler_filter', MyCompilerFilter::class);
AutoloaderOverloader::init('my_compiler_filter');
The Magic™
// index.php
require_once __DIR__ . '/vendor/autoload.php';
stream_filter_register('my_compiler_filter', MyCompilerFilter::class);
AutoloaderOverloader::init('my_compiler_filter');
The Magic™
// index.php
require_once __DIR__ . '/vendor/autoload.php';
stream_filter_register('my_compiler_filter', MyCompilerFilter::class);
AutoloaderOverloader::init('my_compiler_filter');
The Magic™
// index.php
require_once __DIR__ . '/vendor/autoload.php';
stream_filter_register('my_compiler_filter', MyCompilerFilter::class);
AutoloaderOverloader::init('my_compiler_filter');
The Magic™
// index.php
require_once __DIR__ . '/vendor/autoload.php';
stream_filter_register('my_compiler_filter', MyCompilerFilter::class);
AutoloaderOverloader::init('my_compiler_filter');��$class = new MyClass;
$executableCode = transformCode($sourceCodeFile);
$executableCode = transformCode($sourceCodeFile);
eval($executableCode);
$executableCode = transformCode($sourceCodeFile);
eval($executableCode);
eval() is evil* and is disabled on hardened PHP installations.����* is what we’re doing here any better though? 🙄
$executableCode = transformCode($sourceCodeFile);
eval($executableCode);
file_put_contents(
$compiledFile,
$executableCode
);
require $compiledFile;
eval() is evil* and is disabled on hardened PHP installations.����* is what we’re doing here any better though? 🙄
$executableCode = transformCode($sourceCodeFile);
eval($executableCode);
file_put_contents(
$compiledFile,
$executableCode
);
require $compiledFile;
eval() is evil* and is disabled on hardened PHP installations.����* is what we’re doing here any better though? 🙄
But this only works if PHP has permissions to write to disk.
STREAMS
AWESOME
ARE
What Next?
Short Closures!
PHP 7.4 … y so slow?
Pre
Pre
$oddLetterCountWords = array_filter($fruits,� return strlen($fruit) % 2;� );
($fruit) => {��}
Pre
$oddLetterCountWords = array_filter($fruits,� return strlen($fruit) % 2;� );�����$oddLetterCountWords = array_filter($fruits,� return strlen($fruit) % 2;� );
($fruit) => {��}����� function ($fruit) {��}
$ composer require pre/short-closures:^0.8
$ composer require pre/short-closures:^0.8����// Alias for implementing and registering a stream filter.�public function transformCodeFilter($sourceFile) {� return \Pre\Plugin\parse(file_get_contents($sourceFile));�}
Obfuscation
<?php exit('Source Code Protected.'); ?>��aFcZG1BJF0UNAxVSBAgAEREcERF/HxwJRRJTVQlSeSoADhlFHhUADREANR9QLyYMGwYXAxVMABxYeHMFB0VUOxxNFQAbCz8mTw4fCwtLOhw1O1QdA2YBGhpEAFQaCg0pIABTGwoXUwRVbioKH0EdHFRkCAMAGxhUNwBOBxcMGR4AHnNbb05DUlkAAUIYAQYAFRobERcMTw1POzpHOh4GGEVBIUUfGhFTFQBXFwYEBwBTH0xDADMLF1AGHVMLTw8qTUVBTlQAVE9SFhEWBxxFAhxXRTwGAQkfGlMRQEJoFgMZHU9FdwwdCAEPc0FSeQBJUwATZQk=
<?php declare(strict_types=1);
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class DefaultController
{
public function __invoke(Request $request): Response {
return new Response('Hello, World!');
}
}
function transformCodeFilter(string $sourceFile): string {
$source = file_get_contents($sourceFile);
return strpos($source, PREAMBLE_TEXT) !== 0
? $source
: xorStringWithKey(
base64_decode(substr($source, strlen(PREAMBLE_TEXT))),
ENCRYPTION_KEY
);
}
function xorStringWithKey(string $source, string $key): string {
$output = '';
for ($i = 0; $i < strlen($source); $i++) {
$output .= $source[$i] ^ $key[$i % strlen($key)];
}
return $output;
}
function transformCodeFilter(string $sourceFile): string {
$source = file_get_contents($sourceFile);
return strpos($source, PREAMBLE_TEXT) !== 0
? $source
: xorStringWithKey(
base64_decode(substr($source, strlen(PREAMBLE_TEXT))),
ENCRYPTION_KEY
);
}
function xorStringWithKey(string $source, string $key): string {
$output = '';
for ($i = 0; $i < strlen($source); $i++) {
$output .= $source[$i] ^ $key[$i % strlen($key)];
}
return $output;
}
function transformCodeFilter(string $sourceFile): string {
$source = file_get_contents($sourceFile);
return strpos($source, PREAMBLE_TEXT) !== 0
? $source
: xorStringWithKey(
base64_decode(substr($source, strlen(PREAMBLE_TEXT))),
ENCRYPTION_KEY
);
}
function xorStringWithKey(string $source, string $key): string {
$output = '';
for ($i = 0; $i < strlen($source); $i++) {
$output .= $source[$i] ^ $key[$i % strlen($key)];
}
return $output;
}
function transformCodeFilter(string $sourceFile): string {
$source = file_get_contents($sourceFile);
return strpos($source, PREAMBLE_TEXT) !== 0
? $source
: xorStringWithKey(
base64_decode(substr($source, strlen(PREAMBLE_TEXT))),
ENCRYPTION_KEY
);
}
function xorStringWithKey(string $source, string $key): string {
$output = '';
for ($i = 0; $i < strlen($source); $i++) {
$output .= $source[$i] ^ $key[$i % strlen($key)];
}
return $output;
}
function transformCodeFilter(string $sourceFile): string {
$source = file_get_contents($sourceFile);
return strpos($source, PREAMBLE_TEXT) !== 0
? $source
: xorStringWithKey(
base64_decode(substr($source, strlen(PREAMBLE_TEXT))),
ENCRYPTION_KEY
);
}
function xorStringWithKey(string $source, string $key): string {
$output = '';
for ($i = 0; $i < strlen($source); $i++) {
$output .= $source[$i] ^ $key[$i % strlen($key)];
}
return $output;
}
Performance like it’s 1994 🎉
But that’s not the 📌
But that’s not the 📌
But that’s not the 📌
But that’s not the 📌
But that’s not the 📌
But that’s not the 📌
Question.��
Question.
Experiment.�
Question.
Experiment.
Have fun.
Create terrible things.
Z-Engine by Alexander Lisachenko�
github.com/lisachenko/z-engine
Z-Engine uses FFI to access internal structures of PHP itself. It loads the definitions of native PHP structures, like zend_class_entry, zval, etc and manipulates them in runtime.
hey guess what I got working!
SymfonyCon 2020
Thank you!
@ZanBaldwin�🚀 Intergalactic Agency, Vancouver
https://bit.ly/2QF3zh0