Skip to content

Commit

Permalink
Merge pull request #12 from xp-framework/refactor/reflection
Browse files Browse the repository at this point in the history
Migrate to use new reflection API
  • Loading branch information
thekid authored Mar 28, 2024
2 parents c0299e7 + cd8964f commit 0357ec2
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 90 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"keywords": ["module", "xp"],
"require" : {
"xp-framework/core": "^12.0 | ^11.0 | ^10.15",
"xp-framework/reflection": "^3.0 | ^2.9",
"php" : ">=7.0.0"
},
"require-dev" : {
Expand Down
2 changes: 1 addition & 1 deletion src/main/php/util/cmd/Commands.class.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php namespace util\cmd;

use lang\{ClassLoader, ClassNotFoundException, IllegalArgumentException};
use lang\reflect\Package;
use lang\{ClassLoader, ClassNotFoundException, IllegalArgumentException};

/**
* Commands factory. Loads classes, files and named commands by using
Expand Down
77 changes: 37 additions & 40 deletions src/main/php/xp/command/AbstractRunner.class.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php namespace xp\command;

use io\streams\{ConsoleInputStream, ConsoleOutputStream, InputStream, OutputStream, StringReader, StringWriter};
use lang\reflect\TargetInvocationException;
use lang\{ClassLoader, ClassNotFoundException, System, Throwable, XPClass};
use util\cmd\{Commands, Config, Console, ParamString};
use lang\reflection\{InvocationFailed, Type};
use lang\{ClassLoader, ClassNotFoundException, System, Throwable, XPClass, Reflection};
use util\cmd\{Arg, Args, Commands, Config, Console, ParamString};
use xp\runtime\Help;

/**
Expand All @@ -26,9 +26,10 @@ static function __static() {
/**
* Displays usage of command
*
* @param lang.reflection.Type $type
* @return void
*/
protected abstract function commandUsage(XPClass $class);
protected abstract function commandUsage(Type $type);

/**
* Displays usage of runner
Expand Down Expand Up @@ -99,79 +100,75 @@ public function setErr(OutputStream $err) {
*/
protected function runCommand($command, $params, $config) {
try {
$class= Commands::named($command);
$type= Reflection::type(Commands::named($command));
} catch (Throwable $e) {
self::$err->writeLine('*** ', $this->verbose ? $e : $e->getMessage());
return 1;
}

// Usage
if ($params->exists('help', '?')) {
$this->commandUsage($class);
$this->commandUsage($type);
return 0;
}

if ($class->hasMethod('newInstance')) {
$instance= $class->getMethod('newInstance')->invoke(null, [$config]);
} else if ($class->hasConstructor()) {
$instance= $class->newInstance($config);
if ($method= $type->method('newInstance')) {
$instance= $method->invoke(null, [$config]);
} else {
$instance= $class->newInstance();
$instance= $type->newInstance($config);
}

$instance->in= self::$in;
$instance->out= self::$out;
$instance->err= self::$err;

// Arguments
foreach ($class->getMethods() 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 {
foreach ($type->methods() as $method) {
if ($args= $method->annotation(Args::class)) {
if ($select= $args->argument('select')) {
$pass= [];
foreach (preg_split('/, ?/', $method->getAnnotation('args', 'select')) as $def) {
if (is_numeric($def) || '-' == $def[0]) {
foreach (preg_split('/, ?/', $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;
$begin ?? $begin= 0;
$end ?? $end= $params->count - 1;

while ($begin <= $end) {
$pass[]= $params->value($begin++);
}
}
}
} else {
$begin= 0;
$end= $params->count;
$pass= array_slice($params->list, 0, $end);
}

try {
$method->invoke($instance, [$pass]);
} catch (Throwable $e) {
self::$err->writeLine('*** Error for arguments '.$begin.'..'.$end.': ', $this->verbose ? $e : $e->getMessage());
} catch (InvocationFailed $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']);
} else if ($arg= $method->annotation(Arg::class)) {
if (null !== ($position= $arg->argument('position'))) {
$select= (int)$position;
$name= '#'.($position + 1);
$short= null;
} else if (isset($arg['name'])) {
$name= $select= $arg['name'];
$short= $arg['short'] ?? null;
} else {
$name= $select= strtolower(preg_replace('/^set/', '', $method->getName()));
$short= $arg['short'] ?? null;
$select= $name= $arg->argument('name') ?? strtolower(preg_replace('/^set/', '', $method->name()));
$short= $arg->argument('short');
}

if (0 == $method->numParameters()) {
$first= $method->parameter(0);
if (null === $first) {
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!');
if (!$first->optional()) {
self::$err->writeLine("*** Argument {$name} does not exist!");
return 2;
}

Expand All @@ -182,8 +179,8 @@ protected function runCommand($command, $params, $config) {

try {
$method->invoke($instance, $args);
} catch (TargetInvocationException $e) {
self::$err->writeLine('*** Error for argument '.$name.': ', $this->verbose ? $e->getCause() : $e->getCause()->compoundMessage());
} catch (InvocationFailed $e) {
self::$err->writeLine("*** Error for argument {$name}: ", $this->verbose ? $e->getCause() : $e->getCause()->compoundMessage());
return 2;
}
}
Expand Down
30 changes: 15 additions & 15 deletions src/main/php/xp/command/CmdRunner.class.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php namespace xp\command;

use lang\reflect\{Modifiers, Package, TargetInvocationException};
use lang\reflection\Type;
use lang\{ClassLoader, ClassNotFoundException, System, Throwable, XPClass};
use rdbms\ConnectionManager;
use util\cmd\{Commands, Config, ParamString};
Expand Down Expand Up @@ -34,8 +35,8 @@
*
* Pass `-v` to see more verbose output from argument handling.
*
* @test xp://util.cmd.unittest.CmdRunnerTest
* @see xp://util.cmd.Command
* @test util.cmd.unittest.CmdRunnerTest
* @see util.cmd.Command
*/
class CmdRunner extends AbstractRunner {

Expand All @@ -44,29 +45,28 @@ static function __static() { }
/**
* Shows usage
*
* @param lang.XPClass $class
* @param lang.reflection.Type $type
* @return void
*/
protected function commandUsage(XPClass $class) {
$comment= $class->getComment();
protected function commandUsage(Type $type) {
$comment= $type->comment();
if ('' === (string)$comment) {
$markdown= '# '.$class->getSimpleName()."\n\n";
$markdown= '# '.$type->name()."\n\n";
$text= '';
} else {
@list($headline, $text)= explode("\n", $comment, 2);
$markdown= '# '.ltrim($headline, ' #')."\n\n";
}

$markdown.= "- Usage\n ```sh\n$ xp cmd ".Commands::nameOf($class);
$markdown.= "- Usage\n ```sh\n$ xp cmd ".Commands::nameOf($type->class());

$extra= $details= $positional= [];
foreach ($class->getMethods() as $method) {
if (!$method->hasAnnotation('arg')) continue;

$arg= $method->getAnnotation('arg');
$name= strtolower(preg_replace('/^set/', '', $method->getName()));
$optional= 0 === $method->numParameters() || $method->getParameters()[0]->isOptional();
$comment= $method->getComment();
foreach ($type->methods()->annotated(Arg::class) as $method) {
$arg= $method->annotation(Arg::class)->arguments();
$name= strtolower(preg_replace('/^set/', '', $method->name()));
$first= $method->parameter(0);
$optional= $first ? $first->optional() : true;
$comment= $method->comment();

if (isset($arg['position'])) {
$details[$name]= [$comment, null];
Expand Down Expand Up @@ -100,7 +100,7 @@ protected function commandUsage(XPClass $class) {
);
}

Help::render(self::$err, substr($markdown, 0, -1).$text, $class->getClassLoader());
Help::render(self::$err, substr($markdown, 0, -1).$text, $type->classLoader());
}

/**
Expand Down
34 changes: 14 additions & 20 deletions src/main/php/xp/command/Runner.class.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?php namespace xp\command;

use lang\reflect\{Modifiers, Package};
use lang\{ClassLoader, ClassNotFoundException, XPClass};
use lang\reflection\Type;
use lang\{ClassLoader, ClassNotFoundException, XPClass, Reflection};
use util\cmd\{Commands, Config, ParamString};

/**
Expand Down Expand Up @@ -60,33 +61,26 @@ protected static function textOf($markup) {
}

/**
* Show usage
* Shows usage
*
* @param lang.XPClass class
* @param lang.reflection.Type $type
* @return void
*/
protected function commandUsage(XPClass $class) {
protected function commandUsage(Type $type) {

// Description
if (null !== ($comment= $class->getComment())) {
if (null !== ($comment= $type->comment())) {
self::$err->writeLine(self::textOf($comment));
self::$err->writeLine(str_repeat('=', 72));
}

$extra= $details= $positional= [];
foreach ($class->getMethods() as $method) {
if (!$method->hasAnnotation('arg')) continue;

$arg= $method->getAnnotation('arg');
$name= strtolower(preg_replace('/^set/', '', $method->getName()));;
$comment= self::textOf($method->getComment());

if (0 == $method->numParameters()) {
$optional= true;
} else {
list($first, )= $method->getParameters();
$optional= $first->isOptional();
}
foreach ($type->methods()->annotated(Arg::class) as $method) {
$arg= $method->annotation('arg')->arguments();
$name= strtolower(preg_replace('/^set/', '', $method->name()));;
$first= $method->parameter(0);
$optional= $first ? $first->optional() : true;
$comment= self::textOf($method->comment());

if (isset($arg['position'])) {
$details['#'.($arg['position'] + 1)]= $comment;
Expand All @@ -102,7 +96,7 @@ protected function commandUsage(XPClass $class) {

// Usage
asort($positional);
self::$err->write('Usage: $ xpcli ', Commands::nameOf($class), ' ');
self::$err->write('Usage: $ xpcli ', Commands::nameOf($type->class()), ' ');
foreach ($positional as $name) {
self::$err->write('<', $name, '> ');
}
Expand All @@ -124,7 +118,7 @@ protected function commandUsage(XPClass $class) {
* @return void
*/
protected function selfUsage() {
self::$err->writeLine($this->textOf((new XPClass(__CLASS__))->getComment()));
self::$err->writeLine($this->textOf(Reflection::type(self::class)->comment()));
}

/**
Expand Down
Loading

0 comments on commit 0357ec2

Please sign in to comment.