diff --git a/cyr-to-lat.php b/cyr-to-lat.php index 20bd90a..038a179 100644 --- a/cyr-to-lat.php +++ b/cyr-to-lat.php @@ -26,9 +26,9 @@ */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort -/** @noinspection PhpParamsInspection */ +/** @noinspection PhpDefineCanBeReplacedWithConstInspection */ -namespace CyrToLat; +use CyrToLat\Main; if ( ! defined( 'ABSPATH' ) ) { // @codeCoverageIgnoreStart @@ -86,12 +86,24 @@ define( 'CYR_TO_LAT_REQUIRED_MAX_INPUT_VARS', 1000 ); /** - * Init plugin on plugin load. + * Get the instance of the main plugin class. + * + * @return Main */ -require_once constant( 'CYR_TO_LAT_PATH' ) . '/vendor/autoload.php'; -require_once constant( 'CYR_TO_LAT_PATH' ) . '/libs/polyfill-mbstring/bootstrap.php'; +function cyr_to_lat(): Main { + + // Global for backwards compatibility. + global $cyr_to_lat_plugin; -global $cyr_to_lat_plugin; + require_once constant( 'CYR_TO_LAT_PATH' ) . '/vendor/autoload.php'; + require_once constant( 'CYR_TO_LAT_PATH' ) . '/libs/polyfill-mbstring/bootstrap.php'; -$cyr_to_lat_plugin = new Main(); -$cyr_to_lat_plugin->init(); + $cyr_to_lat_plugin = Main::instance(); + + return $cyr_to_lat_plugin; +} + +/** + * Init plugin on plugin load. + */ +cyr_to_lat()->init(); diff --git a/src/php/Main.php b/src/php/Main.php index ecf346a..d13f376 100644 --- a/src/php/Main.php +++ b/src/php/Main.php @@ -131,9 +131,50 @@ class Main { protected $is_frontend; /** - * Main constructor. + * Get a single instance of the plugin. + * + * @return Main + */ + public static function instance(): Main { + static $instance; + + if ( ! $instance ) { + $instance = new self(); + } + + return $instance; + } + + /** + * Init plugin. + * + * @noinspection PhpUndefinedClassInspection */ - public function __construct() { + public function init() { + $this->init_classes(); + + if ( $this->request->is_cli() ) { + try { + /** + * Method WP_CLI::add_command() accepts class as callable. + * + * @noinspection PhpParamsInspection + */ + WP_CLI::add_command( 'cyr2lat', $this->cli ); + } catch ( Exception $ex ) { + return; + } + } + + $this->init_hooks(); + } + + /** + * Init other classes. + * + * @return void + */ + public function init_classes() { $this->request = new Request(); $this->settings = new Settings( [ @@ -171,29 +212,7 @@ public function __construct() { } /** - * Init class. - * - * @noinspection PhpUndefinedClassInspection - */ - public function init() { - if ( $this->request->is_cli() ) { - try { - /** - * Method WP_CLI::add_command() accepts class as callable. - * - * @noinspection PhpParamsInspection - */ - WP_CLI::add_command( 'cyr2lat', $this->cli ); - } catch ( Exception $ex ) { - return; - } - } - - $this->init_hooks(); - } - - /** - * Init class hooks. + * Init hooks. */ public function init_hooks() { if ( $this->is_frontend ) { diff --git a/tests/unit/MainTest.php b/tests/unit/MainTest.php index 66149b4..820c638 100644 --- a/tests/unit/MainTest.php +++ b/tests/unit/MainTest.php @@ -30,7 +30,6 @@ use Exception; use Mockery; use PHPUnit\Runner\Version; -use ReflectionClass; use ReflectionException; use WP_Mock; use WP_REST_Server; @@ -59,16 +58,15 @@ public function tearDown(): void { } /** - * Test constructor + * Test init_classes(). * * @throws ReflectionException Reflection Exception. * * @runInSeparateProcess * @preserveGlobalState disabled */ - public function test_constructor() { - $classname = Main::class; - $frontend = false; + public function test_init_classes() { + $frontend = false; // Test when requirements are met. $requirements_met = true; @@ -97,44 +95,37 @@ function () use ( &$requirements_met ) { Mockery::mock( 'overload:' . WPCli::class ); Mockery::mock( 'overload:' . ACF::class ); - // Get mock, without the constructor being called. - $mock = $this->getMockBuilder( $classname )->disableOriginalConstructor()->getMock(); + $subject = new Main(); + $method = 'init_classes'; - // Now call the constructor. - $reflected_class = new ReflectionClass( $classname ); - $constructor = $reflected_class->getConstructor(); - $constructor->invoke( $mock ); + $subject->$method(); - self::assertInstanceOf( Request::class, $this->get_protected_property( $mock, 'request' ) ); - self::assertInstanceOf( Settings::class, $this->get_protected_property( $mock, 'settings' ) ); - self::assertInstanceOf( AdminNotices::class, $this->get_protected_property( $mock, 'admin_notices' ) ); - self::assertInstanceOf( PostConversionProcess::class, $this->get_protected_property( $mock, 'process_all_posts' ) ); - self::assertInstanceOf( TermConversionProcess::class, $this->get_protected_property( $mock, 'process_all_terms' ) ); - self::assertInstanceOf( Converter::class, $this->get_protected_property( $mock, 'converter' ) ); - self::assertInstanceOf( WPCli::class, $this->get_protected_property( $mock, 'cli' ) ); - self::assertInstanceOf( ACF::class, $this->get_protected_property( $mock, 'acf' ) ); - self::assertSame( $frontend, $this->get_protected_property( $mock, 'is_frontend' ) ); + self::assertInstanceOf( Request::class, $this->get_protected_property( $subject, 'request' ) ); + self::assertInstanceOf( Settings::class, $this->get_protected_property( $subject, 'settings' ) ); + self::assertInstanceOf( AdminNotices::class, $this->get_protected_property( $subject, 'admin_notices' ) ); + self::assertInstanceOf( PostConversionProcess::class, $this->get_protected_property( $subject, 'process_all_posts' ) ); + self::assertInstanceOf( TermConversionProcess::class, $this->get_protected_property( $subject, 'process_all_terms' ) ); + self::assertInstanceOf( Converter::class, $this->get_protected_property( $subject, 'converter' ) ); + self::assertInstanceOf( WPCli::class, $this->get_protected_property( $subject, 'cli' ) ); + self::assertInstanceOf( ACF::class, $this->get_protected_property( $subject, 'acf' ) ); + self::assertSame( $frontend, $this->get_protected_property( $subject, 'is_frontend' ) ); // Test when requirements are not met. $requirements_met = false; - // Get mock, without the constructor being called. - $mock = $this->getMockBuilder( $classname )->disableOriginalConstructor()->getMock(); - - // Now call the constructor. - $reflected_class = new ReflectionClass( $classname ); - $constructor = $reflected_class->getConstructor(); - $constructor->invoke( $mock ); - - self::assertInstanceOf( Request::class, $this->get_protected_property( $mock, 'request' ) ); - self::assertInstanceOf( Settings::class, $this->get_protected_property( $mock, 'settings' ) ); - self::assertInstanceOf( AdminNotices::class, $this->get_protected_property( $mock, 'admin_notices' ) ); - self::assertNull( $this->get_protected_property( $mock, 'process_all_posts' ) ); - self::assertNull( $this->get_protected_property( $mock, 'process_all_terms' ) ); - self::assertNull( $this->get_protected_property( $mock, 'converter' ) ); - self::assertNull( $this->get_protected_property( $mock, 'cli' ) ); - self::assertNull( $this->get_protected_property( $mock, 'acf' ) ); - self::assertNull( $this->get_protected_property( $mock, 'is_frontend' ) ); + $subject = new Main(); + + $subject->$method(); + + self::assertInstanceOf( Request::class, $this->get_protected_property( $subject, 'request' ) ); + self::assertInstanceOf( Settings::class, $this->get_protected_property( $subject, 'settings' ) ); + self::assertInstanceOf( AdminNotices::class, $this->get_protected_property( $subject, 'admin_notices' ) ); + self::assertNull( $this->get_protected_property( $subject, 'process_all_posts' ) ); + self::assertNull( $this->get_protected_property( $subject, 'process_all_terms' ) ); + self::assertNull( $this->get_protected_property( $subject, 'converter' ) ); + self::assertNull( $this->get_protected_property( $subject, 'cli' ) ); + self::assertNull( $this->get_protected_property( $subject, 'acf' ) ); + self::assertNull( $this->get_protected_property( $subject, 'is_frontend' ) ); } /** @@ -147,9 +138,9 @@ public function test_init() { $request->shouldReceive( 'is_cli' )->andReturn( false ); $subject = Mockery::mock( Main::class )->makePartial(); - $this->set_protected_property( $subject, 'request', $request ); - + $subject->shouldReceive( 'init_classes' )->once(); $subject->shouldReceive( 'init_hooks' )->once(); + $this->set_protected_property( $subject, 'request', $request ); $subject->init(); } @@ -165,8 +156,9 @@ public function test_init_with_cli_error() { $request->shouldReceive( 'is_cli' )->andReturn( true ); $subject = Mockery::mock( Main::class )->makePartial(); - $this->set_protected_property( $subject, 'request', $request ); + $subject->shouldReceive( 'init_classes' )->once(); $subject->shouldReceive( 'init_hooks' )->never(); + $this->set_protected_property( $subject, 'request', $request ); $add_command = FunctionMocker::replace( '\WP_CLI::add_command', @@ -191,8 +183,9 @@ public function test_init_with_cli() { $request->shouldReceive( 'is_cli' )->andReturn( true ); $subject = Mockery::mock( Main::class )->makePartial(); - $this->set_protected_property( $subject, 'request', $request ); + $subject->shouldReceive( 'init_classes' )->once(); $subject->shouldReceive( 'init_hooks' )->once(); + $this->set_protected_property( $subject, 'request', $request ); $add_command = FunctionMocker::replace( '\WP_CLI::add_command', @@ -220,9 +213,9 @@ public function test_init_hooks( $polylang, $sitepress, $frontend ) { $request = Mockery::mock( Request::class ); $request->shouldReceive( 'is_allowed' )->andReturn( true ); - $subject = Mockery::mock( Main::class )->makePartial()->shouldAllowMockingProtectedMethods(); + $subject = Mockery::mock( Main::class )->makePartial(); + $subject->shouldAllowMockingProtectedMethods(); $this->set_protected_property( $subject, 'request', $request ); - $this->set_protected_property( $subject, 'is_frontend', $frontend ); WP_Mock::expectFilterAdded( 'sanitize_title', [ $subject, 'sanitize_title' ], 9, 3 ); diff --git a/tests/unit/PluginFileTest.php b/tests/unit/PluginFileTest.php index adcf9db..14ca995 100644 --- a/tests/unit/PluginFileTest.php +++ b/tests/unit/PluginFileTest.php @@ -88,16 +88,11 @@ static function ( $name ) { ); $main = Mockery::mock( 'overload:' . Main::class ); + $main->shouldReceive( 'instance' )->once()->andReturn( $main ); $main->shouldReceive( 'init' )->once(); require PLUGIN_MAIN_FILE; - // Include main file the second time to make sure that plugin is not activated again. - include PLUGIN_MAIN_FILE; - - $defined->wasCalledWithTimes( [ 'ABSPATH' ], 2 ); - $defined->wasCalledWithTimes( [ 'CYR_TO_LAT_VERSION' ], 2 ); - $expected = [ 'version' => CYR_TO_LAT_TEST_VERSION, ];