/** * Lists commands * * @return void */ protected function listCommands() { $commandsIn = function ($package) { $text = ''; foreach ($package->getClasses() as $class) { if ($class->isSubclassOf('util.cmd.Command') && !Modifiers::isAbstract($class->getModifiers())) { $text .= ' $ xpcli ' . $class->getSimpleName() . "\n"; } } return $text ?: ' (no commands)'; }; self::$err->writeLine('Named commands'); self::$err->writeLine(); if ($packages = Commands::allPackages()) { foreach (Commands::allPackages() as $package) { self::$err->writeLine('* ', $package); self::$err->writeLine($commandsIn($package)); } self::$err->writeLine(); } self::$err->writeLine('* Global package'); self::$err->writeLine($commandsIn(Package::forName(null))); }
/** * Runs class * * @param string $command * @param util.cmd.ParamString $params * @param util.cmd.Config $config * @return int */ protected function runCommand($command, $params, $config) { try { $class = Commands::named($command); } catch (Throwable $e) { self::$err->writeLine('*** ', $this->verbose ? $e : $e->getMessage()); return 1; } // Usage if ($params->exists('help', '?')) { $this->commandUsage($class); return 0; } // BC: PropertyManager, Logger, ConnectionManager instances $pm = PropertyManager::getInstance(); $pm->setSources($config->sources()); $l = Logger::getInstance(); $pm->hasProperties('log') && $l->configure($pm->getProperties('log')); if (class_exists('rdbms\\DBConnection')) { // FIXME: Job of XPInjector? $cm = ConnectionManager::getInstance(); $pm->hasProperties('database') && $cm->configure($pm->getProperties('database')); } // Setup logger context for all registered log categories foreach (Logger::getInstance()->getCategories() as $category) { if (null === ($context = $category->getContext()) || !$context instanceof EnvironmentAware) { continue; } $context->setHostname(System::getProperty('host.name')); $context->setRunner(nameof($this)); $context->setInstance($class->getName()); $context->setResource(null); $context->setParams($params->string); } if ($class->hasMethod('newInstance')) { $instance = $class->getMethod('newInstance')->invoke(null, [$config]); } else { if ($class->hasConstructor()) { $instance = $class->newInstance($config); } else { $instance = $class->newInstance(); } } $instance->in = self::$in; $instance->out = self::$out; $instance->err = self::$err; $methods = $class->getMethods(); // Injection foreach ($methods as $method) { if (!$method->hasAnnotation('inject')) { continue; } $inject = $method->getAnnotation('inject'); if (isset($inject['type'])) { $type = $inject['type']; } else { if ($restriction = $method->getParameter(0)->getTypeRestriction()) { $type = $restriction->getName(); } else { $type = $method->getParameter(0)->getType()->getName(); } } try { switch ($type) { case 'rdbms.DBConnection': $args = [$cm->getByHost($inject['name'], 0)]; break; case 'util.Properties': $p = $pm->getProperties($inject['name']); // If a PropertyAccess is retrieved which is not a util.Properties, // then, for BC sake, convert it into a util.Properties if ($p instanceof PropertyAccess && !$p instanceof Properties) { $convert = new Properties(null); $convert->load(new \io\streams\MemoryInputStream('')); $section = $p->getFirstSection(); while ($section) { // HACK: Properties::writeSection() would first attempts to // read the whole file, we cannot make use of it. $convert->_data[$section] = $p->readSection($section); $section = $p->getNextSection(); } $args = [$convert]; } else { $args = [$p]; } break; case 'util.log.LogCategory': $args = [$l->getCategory($inject['name'])]; break; default: self::$err->writeLine('*** Unknown injection type "' . $type . '" at method "' . $method->getName() . '"'); return 2; } $method->invoke($instance, $args); } catch (TargetInvocationException $e) { self::$err->writeLine('*** Error injecting ' . $type . ' ' . $inject['name'] . ': ' . $e->getCause()->compoundMessage()); return 2; } catch (Throwable $e) { self::$err->writeLine('*** Error injecting ' . $type . ' ' . $inject['name'] . ': ' . $e->compoundMessage()); return 2; } } // Arguments foreach ($methods as $method) { if ($method->hasAnnotation('args')) { // Pass all arguments if (!$method->hasAnnotation('args', 'select')) { $begin = 0; $end = $params->count; $pass = array_slice($params->list, 0, $end); } else { $pass = []; foreach (preg_split('/, ?/', $method->getAnnotation('args', 'select')) as $def) { if (is_numeric($def) || '-' == $def[0]) { $pass[] = $params->value((int) $def); } else { sscanf($def, '[%d..%d]', $begin, $end); isset($begin) || ($begin = 0); isset($end) || ($end = $params->count - 1); while ($begin <= $end) { $pass[] = $params->value($begin++); } } } } try { $method->invoke($instance, [$pass]); } catch (Throwable $e) { self::$err->writeLine('*** Error for arguments ' . $begin . '..' . $end . ': ', $this->verbose ? $e : $e->getMessage()); return 2; } } else { if ($method->hasAnnotation('arg')) { // Pass arguments $arg = $method->getAnnotation('arg'); if (isset($arg['position'])) { $name = '#' . ($arg['position'] + 1); $select = intval($arg['position']); $short = null; } else { if (isset($arg['name'])) { $name = $select = $arg['name']; $short = isset($arg['short']) ? $arg['short'] : null; } else { $name = $select = strtolower(preg_replace('/^set/', '', $method->getName())); $short = isset($arg['short']) ? $arg['short'] : null; } } if (0 == $method->numParameters()) { if (!$params->exists($select, $short)) { continue; } $args = []; } else { if (!$params->exists($select, $short)) { list($first, ) = $method->getParameters(); if (!$first->isOptional()) { self::$err->writeLine('*** Argument ' . $name . ' does not exist!'); return 2; } $args = []; } else { $args = [$params->value($select, $short)]; } } try { $method->invoke($instance, $args); } catch (TargetInvocationException $e) { self::$err->writeLine('*** Error for argument ' . $name . ': ', $this->verbose ? $e->getCause() : $e->getCause()->compoundMessage()); return 2; } } } } try { return (int) $instance->run(); } catch (Throwable $t) { self::$err->writeLine('*** ', $t->toString()); return 70; // EX_SOFTWARE according to sysexits.h } }
public function nameOf_shortened_when_package_is_registered() { self::withPackage('util.cmd.unittest', function () { $this->assertEquals('BatchImport', Commands::nameOf(self::$class)); }); }
/** * Lists commands * * @return void */ protected function listCommands() { $commandsIn = function ($package) { $markdown = ''; foreach ($package->getClasses() as $class) { if ($class->isSubclassOf('util.cmd.Command') && !Modifiers::isAbstract($class->getModifiers())) { $markdown .= ' $ xp cmd ' . $class->getSimpleName() . "\n"; } } return $markdown ?: ' *(no commands)*'; }; $markdown = "# Named commands\n\n"; if ($packages = Commands::allPackages()) { foreach ($packages as $package) { $markdown .= '* In package **' . $package->getName() . "**\n\n" . $commandsIn($package); } $markdown .= "\n"; } $markdown .= "* In global package\n\n" . $commandsIn(Package::forName(null)); Help::render(self::$err, $markdown, []); }