Skip to content

WordPress Playground compatibility #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 1, 2024
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ The Blueprints library is distributed as a .phar library. To build the .phar fil
vendor/bin/box compile
```

Note that in box.json, the `"check-requirements"` option is set to `false`. Somehow, keeping it as `true` results in a
.phar file
that breaks HTTP requests in Playground. @TODO: Investigate why this is the case.

To try the built .phar file, run:

```shell
rm -rf new-wp/* && USE_PHAR=1 php blueprint_compiling.php
```

5 changes: 4 additions & 1 deletion blueprint_compiling.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use WordPress\Blueprints\ContainerBuilder;
use WordPress\Blueprints\Model\BlueprintBuilder;
use function WordPress\Blueprints\run_blueprint;

Expand All @@ -21,6 +22,8 @@
'WP_CACHE' => true,
] )
->withPlugins( [
// Required for withContent():
'https://downloads.wordpress.org/plugin/wordpress-importer.zip',
'https://downloads.wordpress.org/plugin/hello-dolly.zip',
'https://downloads.wordpress.org/plugin/gutenberg.17.7.0.zip',
] )
Expand All @@ -37,6 +40,6 @@
->toBlueprint();


$results = run_blueprint( $blueprint, __DIR__ . '/new-wp' );
$results = run_blueprint( $blueprint, ContainerBuilder::ENVIRONMENT_NATIVE, __DIR__ . '/new-wp' );

var_dump( $results );
39 changes: 26 additions & 13 deletions src/WordPress/Blueprints/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,22 @@
use WordPress\Blueprints\Runner\Step\UnzipStepRunner;
use WordPress\Blueprints\Runner\Step\WPCLIStepRunner;
use WordPress\Blueprints\Runner\Step\WriteFileStepRunner;
use WordPress\Blueprints\Runtime\NativePHPRuntime;
use WordPress\Blueprints\Runtime\Runtime;
use WordPress\Blueprints\Runtime\RuntimeInterface;
use WordPress\DataSource\FileSource;
use WordPress\DataSource\PlaygroundFetchSource;
use WordPress\DataSource\ProgressEvent;
use WordPress\DataSource\UrlSource;

class ContainerBuilder {

const RUNTIME_NATIVE = 'native';
const RUNTIME_PLAYGROUND = 'playground';
const RUNTIME_WP_NOW = 'wp-now';
const RUNTIMES = [
self::RUNTIME_NATIVE,
self::RUNTIME_PLAYGROUND,
self::RUNTIME_WP_NOW,
const ENVIRONMENT_NATIVE = 'native';
const ENVIRONMENT_PLAYGROUND = 'playground';
const ENVIRONMENT_WP_NOW = 'wp-now';
const ENVIRONMENTS = [
self::ENVIRONMENT_NATIVE,
self::ENVIRONMENT_PLAYGROUND,
self::ENVIRONMENT_WP_NOW,
];

protected $container;
Expand All @@ -83,13 +84,13 @@ public function __construct() {
}


public function build( RuntimeInterface $runtime ) {
public function build( string $environment, RuntimeInterface $runtime ) {
$container = $this->container;
$container['runtime'] = function () use ( $runtime ) {
return $runtime;
};

if ( $runtime instanceof NativePHPRuntime ) {
if ( $environment === static::ENVIRONMENT_NATIVE ) {
$container['downloads_cache'] = function ( $c ) {
return new FileCache();
};
Expand All @@ -101,6 +102,18 @@ public function build( RuntimeInterface $runtime ) {
echo $event->url . ' ' . $event->downloadedBytes . '/' . $event->totalBytes . " \r";
};
};
$container[ "resource.resolver." . UrlResource::DISCRIMINATOR ] = function ( $c ) {
return new UrlResourceResolver( $c['data_source.url'] );
};
} elseif ( $environment === static::ENVIRONMENT_PLAYGROUND ) {
$container[ "resource.resolver." . UrlResource::DISCRIMINATOR ] = function ( $c ) {
return new UrlResourceResolver( $c['data_source.playground_fetch'] );
};
$container['progress_reporter'] = function ( $c ) {
return function ( ProgressEvent $event ) {
echo $event->url . ' ' . $event->downloadedBytes . '/' . $event->totalBytes . " \r";
};
};
} else {
throw new InvalidArgumentException( "Not implemented yet" );
}
Expand Down Expand Up @@ -218,9 +231,6 @@ function () use ( $c ) {
return new RunSQLStepRunner();
};

$container[ "resource.resolver." . UrlResource::DISCRIMINATOR ] = function ( $c ) {
return new UrlResourceResolver( $c['data_source.url'] );
};
$container[ "resource.resolver." . FilesystemResource::DISCRIMINATOR ] = function () {
return new FilesystemResourceResolver();
};
Expand Down Expand Up @@ -262,6 +272,9 @@ function () use ( $c ) {
$container['data_source.url'] = function ( $c ) {
return new UrlSource( $c['http_client'], $c['downloads_cache'] );
};
$container['data_source.playground_fetch'] = function ( $c ) {
return new PlaygroundFetchSource();
};

// Add a progress listener to all data sources
foreach ( $container->keys() as $key ) {
Expand Down
2 changes: 1 addition & 1 deletion src/WordPress/Blueprints/Model/BlueprintBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function withContent( $wxrs ) {
if ( ! is_array( $wxrs ) ) {
$wxrs = [ $wxrs ];
}
$this->withPlugin( 'https://downloads.wordpress.org/plugin/wordpress-importer.zip' );
// @TODO: Should this automatically add the importer plugin if it's not already installed?
foreach ( $wxrs as $wxr ) {
$this->addStep(
( new ImportFileStep() )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function run( SetSiteOptionsStep $input, Tracker $tracker ) {
// with a separate wp-cli command.
return $this->getRuntime()->evalPhpInSubProcess( <<<'CODE'
<?php
require 'wp-load.php';
require getenv('DOCROOT'). '/wp-load.php';
$site_options = getenv("OPTIONS") ? json_decode(getenv("OPTIONS"), true) : [];
foreach($site_options as $name => $value) {
update_option($name, $value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Symfony\Component\Process\Process;
use function WordPress\Blueprints\join_paths;

class NativePHPRuntime implements RuntimeInterface {
class Runtime implements RuntimeInterface {

public Filesystem $fs;

Expand All @@ -33,7 +33,7 @@ public function getDocumentRoot(): string {
}

public function resolvePath( string $path ): string {
return Path::makeAbsolute($path, $this->getDocumentRoot());
return Path::makeAbsolute( $path, $this->getDocumentRoot() );
}

public function withTemporaryDirectory( $callback ) {
Expand Down
9 changes: 4 additions & 5 deletions src/WordPress/Blueprints/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
namespace WordPress\Blueprints;

use Symfony\Component\Filesystem\Exception\IOException;
use WordPress\Blueprints\Runtime\NativePHPRuntime;
use WordPress\Blueprints\Runtime\Runtime;

function run_blueprint( $json, $documentRoot = '/wordpress' ) {
function run_blueprint( $json, $environment, $documentRoot = '/wordpress' ) {
$c = ( new ContainerBuilder() )->build(
new NativePHPRuntime(
$documentRoot
)
$environment,
new Runtime( $documentRoot )
);

return $c['blueprint.engine']->runBlueprint( $json );
Expand Down
40 changes: 40 additions & 0 deletions src/WordPress/DataSource/PlaygroundFetchSource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace WordPress\DataSource;

use Psr\SimpleCache\CacheInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpClient\Response\StreamWrapper;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use WordPress\Streams\StreamPeeker;
use WordPress\Streams\StreamPeekerContext;

class PlaygroundFetchSource extends BaseDataSource {

public $proc_handles = [];

public function stream( $resourceIdentifier ) {
$url = $resourceIdentifier;
$proc_handle = proc_open(
[ 'fetch', $url, ],
[ 1 => [ 'pipe', 'w' ], 2 => [ 'pipe', 'w' ] ],
$pipes
);
// This prevents the process handle from getting garbage collected and
// breaking the stdout pipe. However, how the program never terminates.
// Presumably we need to peek() on the resource handle and close the
// process handle when it's done.
// Without this line, we get the following error:
// PHP Fatal error: Uncaught TypeError: stream_copy_to_stream(): supplied resource is not a valid stream resource i
// var_dump()–ing first says
// resource(457) of type (stream)
// but then it says
// resource(457) of type (Unknown)
$this->proc_handles[] = $proc_handle;

return $pipes[1];
}

}

6 changes: 3 additions & 3 deletions src/WordPress/DataSource/UrlSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function stream( $resourceIdentifier ) {
if ( $this->cache->has( $url ) ) {
// Return a stream resource.
// @TODO: Stream directly from the cache
$cached = $this->cache->get( $url );
$cached = $this->cache->get( $url );
$data_size = strlen( $cached );
$this->events->dispatch( new ProgressEvent(
$url,
Expand All @@ -60,17 +60,17 @@ public function stream( $resourceIdentifier ) {
) );
},
] );
$stream = StreamWrapper::createResource( $response, $this->client );
$stream = StreamWrapper::createResource( $response, $this->client );
if ( ! $stream ) {
throw new \Exception( 'Failed to download file' );
}
$onChunk = function ( $chunk ) use ( $url, $response, $stream ) {
// Handle response caching
// @TODO: don't buffer, just keep appending to the cache.
static $bufferedChunks = [];
$bufferedChunks[] = $chunk;
if ( feof( $stream ) ) {
$this->cache->set( $url, implode( '', $bufferedChunks ) );
$bufferedChunks = [];
}
};
$onClose = function () use ( $response ) {
Expand Down
16 changes: 8 additions & 8 deletions src/WordPress/Zip/ZipStreamReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ static public function readEntry( $fp ) {
* @param resource $stream
*/
static protected function readFileEntry( $stream ): ZipFileEntry {
$data = self::read_bytes( $stream, 26 );
$data = unpack( 'vversionNeeded/vgeneralPurpose/vcompressionMethod/vlastModifiedTime/vlastModifiedDate/Vcrc/VcompressedSize/VuncompressedSize/vpathLength/vextraLength',
$data = self::read_bytes( $stream, 26 );
$data = unpack( 'vversionNeeded/vgeneralPurpose/vcompressionMethod/vlastModifiedTime/vlastModifiedDate/Vcrc/VcompressedSize/VuncompressedSize/vpathLength/vextraLength',
$data );
$path = self::read_bytes( $stream, $data['pathLength'] );
$path = self::read_bytes( $stream, $data['pathLength'] );
$extra = self::read_bytes( $stream, $data['extraLength'] );
$bytes = self::read_bytes( $stream, $data['compressedSize'] );

Expand Down Expand Up @@ -119,11 +119,11 @@ static protected function readFileEntry( $stream ): ZipFileEntry {
* @param resource stream
*/
static protected function readCentralDirectoryEntry( $stream ): ZipCentralDirectoryEntry {
$data = static::read_bytes( $stream, 42 );
$data = unpack( 'vversionCreated/vversionNeeded/vgeneralPurpose/vcompressionMethod/vlastModifiedTime/vlastModifiedDate/Vcrc/VcompressedSize/VuncompressedSize/vpathLength/vextraLength/vfileCommentLength/vdiskNumber/vinternalAttributes/VexternalAttributes/VfirstByteAt',
$data = static::read_bytes( $stream, 42 );
$data = unpack( 'vversionCreated/vversionNeeded/vgeneralPurpose/vcompressionMethod/vlastModifiedTime/vlastModifiedDate/Vcrc/VcompressedSize/VuncompressedSize/vpathLength/vextraLength/vfileCommentLength/vdiskNumber/vinternalAttributes/VexternalAttributes/VfirstByteAt',
$data );
$path = static::read_bytes( $stream, $data['pathLength'] );
$extra = static::read_bytes( $stream, $data['extraLength'] );
$path = static::read_bytes( $stream, $data['pathLength'] );
$extra = static::read_bytes( $stream, $data['extraLength'] );
$fileComment = static::read_bytes( $stream, $data['fileCommentLength'] );

return new ZipCentralDirectoryEntry(
Expand Down Expand Up @@ -204,7 +204,7 @@ static protected function read_bytes( $stream, $length ): string|bool {
return false;
}
$length -= strlen( $chunk );
$data .= $chunk;
$data .= $chunk;

if ( $length === 0 ) {
break;
Expand Down
2 changes: 1 addition & 1 deletion src/WordPress/Zip/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function zip_extract_to( $fp, $toPath ) {
if ( ! $entry->isFileEntry() ) {
continue;
}
$path = $toPath . '/' . sanitize_path( $entry->path );
$path = $toPath . '/' . sanitize_path( $entry->path );
$parent = dirname( $path );
if ( ! is_dir( $parent ) ) {
mkdir( $parent, 0777, true );
Expand Down
Loading