<?php
/**
* Handles generation and deletion of the bootstrap for the standalone WAF mode.
*
* @package automattic/jetpack-waf
*/
namespace Automattic\Jetpack\Waf;
use Composer\InstalledVersions;
use Exception;
/**
* Handles the bootstrap.
*/
class Waf_Standalone_Bootstrap {
/**
* Ensures that constants are initialized if this class is used.
*/
public function __construct() {
$this->guard_against_missing_abspath();
$this->initialize_constants();
}
/**
* Ensures that this class is not used unless we are in the right context.
*
* @return void
* @throws Exception If we are outside of WordPress.
*/
private function guard_against_missing_abspath() {
if ( ! defined( 'ABSPATH' ) ) {
throw new Exception( 'Cannot generate the WAF bootstrap if we are not running in WordPress context.' );
}
}
/**
* Initializes the constants required for generating the bootstrap, if they have not been initialized yet.
*
* @return void
*/
private function initialize_constants() {
Waf_Constants::initialize_constants();
}
/**
* Initialized the WP filesystem and serves as a mocking hook for tests.
*
* @return void
*/
protected function initialize_filesystem() {
if ( ! function_exists( '\\WP_Filesystem' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
WP_Filesystem();
}
/**
* Finds the path to the autoloader, which can then be used to require the autoloader in the generated boostrap file.
*
* @return string|null
* @throws Exception In case the autoloader file can not be found.
*/
private function locate_autoloader_file() {
global $jetpack_autoloader_loader;
$autoload_file = null;
// Try the Jetpack autoloader.
if ( isset( $jetpack_autoloader_loader ) ) {
$class_file = $jetpack_autoloader_loader->find_class_file( Waf_Runner::class );
if ( $class_file ) {
$autoload_file = dirname( dirname( dirname( dirname( dirname( $class_file ) ) ) ) ) . '/vendor/autoload.php';
}
}
// Try Composer's autoloader.
if ( null === $autoload_file
&& is_callable( array( InstalledVersions::class, 'getInstallPath' ) )
&& InstalledVersions::isInstalled( 'automattic/jetpack-waf' )
) {
$package_file = InstalledVersions::getInstallPath( 'automattic/jetpack-waf' );
if ( substr( $package_file, -23 ) === '/automattic/jetpack-waf' ) {
$autoload_file = dirname( dirname( dirname( $package_file ) ) ) . '/vendor/autoload.php';
}
}
// Guess. First look for being in a `vendor/automattic/jetpack-waf/src/', then see if we're standalone with our own vendor dir.
if ( null === $autoload_file ) {
$autoload_file = dirname( dirname( dirname( dirname( __DIR__ ) ) ) ) . '/vendor/autoload.php';
if ( ! file_exists( $autoload_file ) ) {
$autoload_file = dirname( __DIR__ ) . '/vendor/autoload.php';
}
}
// Check that the determined file actually exists.
if ( ! file_exists( $autoload_file ) ) {
throw new Exception( 'Can not find autoloader, and the WAF standalone boostrap will not work without it.' );
}
return $autoload_file;
}
/**
* Gets the path to the bootstrap.php file.
*
* @return string The bootstrap.php file path.
*/
public function get_bootstrap_file_path() {
return trailingslashit( JETPACK_WAF_DIR ) . 'bootstrap.php';
}
/**
* Generates the bootstrap file.
*
* @return string Absolute path to the bootstrap file.
* @throws Exception In case the file can not be written.
*/
public function generate() {
$this->initialize_filesystem();
global $wp_filesystem;
if ( ! $wp_filesystem ) {
throw new Exception( 'Can not work without the file system being initialized.' );
}
$bootstrap_file = $this->get_bootstrap_file_path();
$mode_option = get_option( Waf_Runner::MODE_OPTION_NAME, false );
$share_data_option = get_option( Waf_Runner::SHARE_DATA_OPTION_NAME, false );
// phpcs:disable WordPress.PHP.DevelopmentFunctions
$code = "<?php\n"
. sprintf( "define( 'DISABLE_JETPACK_WAF', %s );\n", var_export( defined( 'DISABLE_JETPACK_WAF' ) && DISABLE_JETPACK_WAF, true ) )
. "if ( defined( 'DISABLE_JETPACK_WAF' ) && DISABLE_JETPACK_WAF ) return;\n"
. sprintf( "define( 'JETPACK_WAF_MODE', %s );\n", var_export( $mode_option ? $mode_option : 'silent', true ) )
. sprintf( "define( 'JETPACK_WAF_SHARE_DATA', %s );\n", var_export( $share_data_option, true ) )
. sprintf( "define( 'JETPACK_WAF_DIR', %s );\n", var_export( JETPACK_WAF_DIR, true ) )
. sprintf( "define( 'JETPACK_WAF_WPCONFIG', %s );\n", var_export( JETPACK_WAF_WPCONFIG, true ) )
. 'require_once ' . var_export( $this->locate_autoloader_file(), true ) . ";\n"
. "Automattic\Jetpack\Waf\Waf_Runner::initialize();\n";
// phpcs:enable
if ( ! $wp_filesystem->is_dir( JETPACK_WAF_DIR ) ) {
if ( ! $wp_filesystem->mkdir( JETPACK_WAF_DIR ) ) {
throw new Exception( 'Failed creating WAF standalone bootstrap file directory: ' . JETPACK_WAF_DIR );
}
}
if ( ! $wp_filesystem->put_contents( $bootstrap_file, $code ) ) {
throw new Exception( 'Failed writing WAF standalone bootstrap file to: ' . $bootstrap_file );
}
return $bootstrap_file;
}
}