xp-framework/rfc

XP Class Runner

thekid opened this issue · 0 comments

Scope of Change

A runner script (similar to the unittest runner) will be created to run
classes as scripts.

Rationale

Simplify command line utilities development.

Functionality

Overview

Instead of writing a complete script we now declare a class as follows:

<?php
  class DumpNews extends Command {
    var
      $id   = 0,
      $conn = NULL;

    #[@arg(position= 0)]
    function setNewsId($id) {
      $this->id= $id;
    }

    #[@inject(type= 'rdbms.DBConnection', name= 'news')]  
    function setConnection(&$conn) {
      $this->conn= &$conn;
    }

    function run() {
      $this->out->writeLine(xp::stringOf($conn->select(
        '* from news where news_id= %d', 
        $this->id
      )));
    }    
  }
?>

We can run this class by using the cli-runner:

  $ xpcli -c ~/.xp/news/ de.thekid.blog.DumpNews 1

Rules

* The class must extend the util.cmd.Command class

* Arguments and resources are injected by means of annotated methods

* We use an output stream to write data

* The class can be reused in different environments because it is not 
  dependant on anything console-specific.

The xpcli runner

Usage:

  $ xpcli [runner-args]* [fully.qualified.ClassName] [class-args]*

The runner-args are any arguments beginning with a dash ("-"). The class
name argument is the first argument without a dash in front of it.

Runner-args:

* -c | --config
  Path to configuration files, defaults to "etc/".

Class-args:

* -? | --help
  Always supported. Shows a usage generated from the @arg-methods' apidoc
  comments.

The runner goes through the following steps when invoked:

  1. Extract runner arguments

  2. Load command class

  3. Check for -? or --help and show a usage

  4. Configure the property manager

  5. In case a database.ini exists in the property manager's configuration
    file path, configure the connection manager with it

  6. In case a log.ini exists in the property manager's configuration
    file path, configure the logger with it

  7. Perform dependency injection. In case an exception is thrown from any
    of the methods, stop.

  8. Pass arguments. In case an argument is missing or an exception is thrown
    from any of the methods, stop.

  9. Invoke the run() method

The util.cmd.Command class

<?php
  class Command extends Object {
    var
      $out = NULL,
      $err = NULL;

    /**
     * Run method
     *
     * @model   abstract
     * @access  public
     */
    function run() { }
  }
?>

The out and err members are created by the runner and are
PrintOutputStream objects. For the console runner, they write data
to STDOUT and STDERR.

The @arg annotation

This annotation marks methods as parameter acceptors.

* Methods that wish to accept a command line argument must have exactly 
  one argument.

* This argument may have a default value.

* If the method does not have a default value, the parameter passing
  stops hard at this point.

* Otherwise, the method is invoked without arguments, so that the
  default value will be passed.

How to accept arguments:

  1. Positional argument
<?php
  #[@arg(position= 0)]
  function setFirstArgument($v) { }
?>

This will take the first command line argument

  1. Named argument
<?php
  #[@arg(name= 'verbosity')]
  function setVerbosity($v) { }
?>

This will take the value passed as --verbosity= or -v

  1. Auto-named argument
<?php
  #[@arg]
  function setClassname($v) { }
?>

This will take the value passed as --classname= or -c

The argument name is calculated by using a lowercase version of the
method name and stripping a leading "set".

  1. Existance argument
<?php
  #[@arg]
  function setDebug() { 
    $this->debug= TRUE;
  }
?>

This method will be called only if --debug is supplied.

The argument name is calculated by using a lowercase version of the
method name and stripping a leading "set".

Note for cases 2), 3) and 4): The short argument's name is calculated by using 
the first character of the long argument's name. It can be overwritten to
resolve ambiguous names by adding a short= '[CHARACTER]' to the @arg
annotation.

The @Inject annotation

This annotation marks methods as resource injectors.

* Resources are one of the following: database connections, logger 
  categories, properties.

* Resources are configured by property objects:
  - The property manager's lookup path is set to "etc/" per default
    and may be set via xpcli -c <path>
  - The connection manager is configured by database.ini
  - The logger is configured by log.ini

  1. Injecting database connections
<?php
  #[@inject(type= 'rdbms.DBConnection', name= 'news')]  
  function setConnection(&$conn) { }
?>

This method will receive a DBConnection instance (via
ConnectionManager::getInstance()->getByHost('news', 0))

  1. Injecting logger categories
<?php
  #[@inject(type= 'util.log.LogCategory', name= 'default')]  
  function setTrace(&$cat) { }
?>

This method will receive a LogCategory instance (via
Logger::getInstance()->getCategory('default'))

  1. Injecting properties
<?php
  #[@inject(type= 'util.Properties', name= 'app')]  
  function setApplicationConfig(&$conf) { }
?>

This method will receive a LogCategory instance (via
PropertyManager::getInstance()->getProperties('app'))

Security considerations

n/a

Speed impact

n/a

Dependencies

  • xp-framework/rfc #88
    I/O Streams API

Related documents