From 7ae00613bd5985075c0abd5f3bd3d257296d9dc0 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 8 Mar 2023 15:20:33 -0800 Subject: [PATCH 1/4] BootCommand - Add support for `--level=cms-full` --- bin/cv | 1 + src/CmsBootstrap.php | 20 ++++++++++++++++++++ src/Command/BootCommand.php | 4 ++++ tests/Command/BootCommandTest.php | 16 ++++++++++++++-- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/bin/cv b/bin/cv index 9435b992..59c5eb8b 100755 --- a/bin/cv +++ b/bin/cv @@ -18,6 +18,7 @@ $autoloaders = array( foreach ($autoloaders as $autoloader) { if (file_exists($autoloader)) { require_once $autoloader; + define('CV_AUTOLOAD', $autoloader); $found = 1; break; } diff --git a/src/CmsBootstrap.php b/src/CmsBootstrap.php index ad4608a7..592cd467 100644 --- a/src/CmsBootstrap.php +++ b/src/CmsBootstrap.php @@ -103,6 +103,26 @@ public function __construct($options = array()) { $this->addOptions($options); } + /** + * Export bootstrap logic. + * + * @param array $actions + * List of bootstrap actions to include. + * Ex: ['bootCms', 'bootCivi'] + * @return string + * PHP code to `CmsBootstrap` in a new process + */ + public function generate(array $actions = []): string { + $instanceExpr = '\\' . get_class($this) . '::singleton()'; + $code = ''; + $code .= sprintf("require_once %s;\n", var_export(CV_AUTOLOAD, TRUE)); + $code .= sprintf("%s->addOptions(%s);\n", $instanceExpr, var_export($this->getOptions(), TRUE)); + foreach ($actions as $action) { + $code .= sprintf("%s->%s()->bootCivi();\n", $instanceExpr, $action); + } + return $code; + } + /** * Bootstrap the CiviCRM runtime. * diff --git a/src/Command/BootCommand.php b/src/Command/BootCommand.php index 9b10b38b..fe9b92fe 100644 --- a/src/Command/BootCommand.php +++ b/src/Command/BootCommand.php @@ -36,6 +36,10 @@ protected function execute(InputInterface $input, OutputInterface $output) { . '\CRM_Utils_System::loadBootStrap(array(), FALSE);'; break; + case 'cms-full': + $code = \Civi\Cv\CmsBootstrap::singleton()->generate(['bootCms', 'bootCivi']); + break; + case 'none': break; diff --git a/tests/Command/BootCommandTest.php b/tests/Command/BootCommandTest.php index 02e8a3f5..6c1cfd41 100644 --- a/tests/Command/BootCommandTest.php +++ b/tests/Command/BootCommandTest.php @@ -13,8 +13,8 @@ public function setUp(): void { parent::setUp(); } - public function testBootDefault() { - $phpBoot = Process::runOk($this->cv("php:boot")); + public function testBootFull() { + $phpBoot = Process::runOk($this->cv("php:boot --level=full")); $this->assertRegExp(';CIVICRM_SETTINGS_PATH;', $phpBoot->getOutput()); $helloPhp = escapeshellarg($phpBoot->getOutput() @@ -24,6 +24,18 @@ public function testBootDefault() { $this->assertRegExp('/^count is [0-9]+$/', $phpRun->getOutput()); } + public function testBootCmsFull() { + $phpBoot = Process::runOk($this->cv("php:boot --level=cms-full")); + $this->assertRegExp(';BEGINPHP;', $phpBoot->getOutput()); + $this->assertRegExp(';ENDPHP;', $phpBoot->getOutput()); + + $helloPhp = escapeshellarg($phpBoot->getOutput() + . 'printf("count is %s\n", CRM_Core_DAO::singleValueQuery("select count(*) from civicrm_contact"));' + ); + $phpRun = Process::runOk(new \Symfony\Component\Process\Process("php -r $helloPhp")); + $this->assertRegExp('/^count is [0-9]+$/', $phpRun->getOutput()); + } + public function testBootClassLoader() { $phpBoot = Process::runOk($this->cv("php:boot --level=classloader")); $this->assertRegExp(';ClassLoader;', $phpBoot->getOutput()); From eb1c4d0d32b5031f6b0b075925d1a077801de16f Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 9 Mar 2023 18:33:47 -0800 Subject: [PATCH 2/4] Add tests for populating $_CV --- tests/Command/BootCommandTest.php | 8 ++++++-- tests/Command/EvalCommandTest.php | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/Command/BootCommandTest.php b/tests/Command/BootCommandTest.php index 6c1cfd41..92ccc543 100644 --- a/tests/Command/BootCommandTest.php +++ b/tests/Command/BootCommandTest.php @@ -19,9 +19,11 @@ public function testBootFull() { $helloPhp = escapeshellarg($phpBoot->getOutput() . 'printf("count is %s\n", CRM_Core_DAO::singleValueQuery("select count(*) from civicrm_contact"));' + . 'printf("my admin is %s\n", $GLOBALS["_CV"]["ADMIN_USER"]);' ); $phpRun = Process::runOk(new \Symfony\Component\Process\Process("php -r $helloPhp")); - $this->assertRegExp('/^count is [0-9]+$/', $phpRun->getOutput()); + $this->assertRegExp('/^count is [0-9]+\n/', $phpRun->getOutput()); + $this->assertRegExp('/my admin is \w+\n/', $phpRun->getOutput()); } public function testBootCmsFull() { @@ -31,9 +33,11 @@ public function testBootCmsFull() { $helloPhp = escapeshellarg($phpBoot->getOutput() . 'printf("count is %s\n", CRM_Core_DAO::singleValueQuery("select count(*) from civicrm_contact"));' + . 'printf("my admin is %s\n", $GLOBALS["_CV"]["ADMIN_USER"]);' ); $phpRun = Process::runOk(new \Symfony\Component\Process\Process("php -r $helloPhp")); - $this->assertRegExp('/^count is [0-9]+$/', $phpRun->getOutput()); + $this->assertRegExp('/^count is [0-9]+\n/', $phpRun->getOutput()); + $this->assertRegExp('/my admin is \w+\n/', $phpRun->getOutput()); } public function testBootClassLoader() { diff --git a/tests/Command/EvalCommandTest.php b/tests/Command/EvalCommandTest.php index 746a0675..a0f48347 100644 --- a/tests/Command/EvalCommandTest.php +++ b/tests/Command/EvalCommandTest.php @@ -40,6 +40,18 @@ public function testPhpEval_ExitCodeError() { $this->assertEquals(255, $p->getExitCode()); } + public function testPhpEval_CvVar_Full() { + $helloPhp = escapeshellarg('printf("my admin is %s\n", $GLOBALS["_CV"]["ADMIN_USER"]);'); + $p = Process::runOk($this->cv("ev --level=full $helloPhp")); + $this->assertRegExp('/^my admin is \w+\s*$/', $p->getOutput()); + } + + public function testPhpEval_CvVar_CmsFull() { + $helloPhp = escapeshellarg('printf("my admin is %s\n", $GLOBALS["_CV"]["ADMIN_USER"]);'); + $p = Process::runOk($this->cv("ev --level=cms-full $helloPhp")); + $this->assertRegExp('/^my admin is \w+\s*$/', $p->getOutput()); + } + public function testBoot() { $checkBoot = escapeshellarg('echo (function_exists("drupal_add_js") || function_exists("wp_redirect") || class_exists("JFactory") || class_exists("Drupal")) ? "found" : "not-found";'); From 33c296a8a518ee725e4273075d26493d76f40b68 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 9 Mar 2023 18:51:58 -0800 Subject: [PATCH 3/4] BuildkitReader - Fix loading when `CIVICRM_SETTINGS_PATH` is relative --- src/BuildkitReader.php | 4 ++++ src/Util/Filesystem.php | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/BuildkitReader.php b/src/BuildkitReader.php index 7c59b77d..47da5658 100644 --- a/src/BuildkitReader.php +++ b/src/BuildkitReader.php @@ -1,6 +1,8 @@ toAbsolutePath($settingsFile); $parts = explode('/', str_replace('\\', '/', $settingsFile)); while (!empty($parts)) { $last = array_pop($parts); diff --git a/src/Util/Filesystem.php b/src/Util/Filesystem.php index d9b36dab..a0bdcc9a 100644 --- a/src/Util/Filesystem.php +++ b/src/Util/Filesystem.php @@ -9,19 +9,34 @@ public static function exists(?string $path): bool { return $path !== NULL && file_exists($path); } + /** + * @return false|string + */ + public function pwd() { + // exec(pwd) works better with symlinked source trees, but it's + // probably not portable to Windows. + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + return getcwd(); + } + else { + exec('pwd', $output); + return trim(implode("\n", $output)); + } + } + /** * @param string $path * @return string updated $path */ public function toAbsolutePath($path) { if (empty($path)) { - $res = getcwd(); + $res = $this->pwd(); } elseif ($this->isAbsolutePath($path)) { $res = $path; } else { - $res = getcwd() . DIRECTORY_SEPARATOR . $path; + $res = $this->pwd() . DIRECTORY_SEPARATOR . $path; } if (is_dir($res)) { return realpath($res); From 13163db6ff444e017bfdfbe93d1d2333f7ac7801 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 9 Mar 2023 18:52:59 -0800 Subject: [PATCH 4/4] CmsBootstrap - Populate global $_CV (similar to traditional `Bootstrap`) --- src/CmsBootstrap.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/CmsBootstrap.php b/src/CmsBootstrap.php index 592cd467..52970a9d 100644 --- a/src/CmsBootstrap.php +++ b/src/CmsBootstrap.php @@ -238,9 +238,24 @@ public function bootCivi() { \CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone(); } + $GLOBALS['_CV'] = $this->buildCv(); + return $this; } + protected function buildCv(): array { + $settings = constant('CIVICRM_SETTINGS_PATH'); + if ($settings && class_exists('Civi\Cv\SiteConfigReader')) { + $this->writeln("Load supplemental configuration for \"$settings\"", OutputInterface::VERBOSITY_DEBUG); + $reader = new SiteConfigReader($settings); + return $reader->compile(array('buildkit', 'home')); + } + else { + $this->writeln("Warning: Not loading supplemental configuration for \"$settings\". SiteConfigReader is missing.", OutputInterface::VERBOSITY_DEBUG); + return []; + } + } + /** */ protected function loginStandaloneUser() {