Initial commit

This commit is contained in:
Inigo Flores 2016-11-14 21:33:22 +01:00
commit d1f1e4b341
67 changed files with 92949 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
# User specific & automatically generated files #
#################################################
.idea/
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
*~

114
README.md Normal file
View File

@ -0,0 +1,114 @@
# ds-codigos-postales
Dataset que proporciona un listado de todos los códigos postales de España asociados al código INE del municipio al que pertenecen.
## Codigos Postales por Municipio
- Fuente: [Callejero del Censo Electoral (INE)](http://www.ine.es/ss/Satellite?L=es_ES&c=Page&cid=1254735624326&p=1254735624326&pagename=ProductosYServicios%2FPYSLayout)
- URL: `http://www.ine.es/prodyser/callejero/caj_esp/caj_esp_01[YYYY].zip` donde `YYYY` es el último año
- Tipo: Texto de ancho fijo comprimido (.zip)
- Datos procesados: [/data/codigos_postales_municipios.csv](data/codigos_postales_municipios.csv)
### Formato de los datos
Ejemplo en CSV:
| codigo_postal | municipio_id | nombre_entidad_singular |
|---------------|--------------|---------------------------|
| 28100 | 28006 | ALCOBENDAS |
| 28108 | 28006 | ALCOBENDAS |
| 28109 | 28006 | ALCOBENDAS |
| 28110 | 28009 | "DEHESA NUEVA" |
| 28120 | 28045 | "PUEBLAS (LAS)" |
| 28124 | 28124 | "ROBLEDILLO DE LA JARA" |
| 28130 | 28162 | MIRAVAL |
| 28140 | 28059 | ADOBERAS |
| 28150 | 28164 | "MIRADOR (EL)" |
| 28160 | 28145 | "MORALEJA (LA)" |
| 28170 | 28163 | "COTO DE SAN BENITO" |
| 28180 | 28151 | "TOMILLARES (LOS)" |
| 28189 | 28153 | "SOTO (EL)" |
| 28190 | 28118 | "PUEBLA DE LA SIERRA" |
| 28191 | 28117 | "PRADENA DEL RINCON" |
| 28192 | 28021 | "DEHESA BOYAL" |
| 28193 | 28039 | "CERCADOS (LOS)" |
| 28194 | 28124 | "VILLAR (EL)" |
| 28195 | 28902 | "SERRADA DE LA FUENTE" |
| 28196 | 28902 | "PRESA DE PUENTES VIEJAS" |
| 28200 | 28131 | "JURISDICCION (LA)" |
| 28210 | 28160 | VALDEMORILLO |
| 28211 | 28054 | PERALEJO |
| 28212 | 28095 | "BARRANCOS (LOS)" |
## Codigos Postales por Municipio (Histórico)
- Fuente: [Callejero del Censo Electoral (INE)](http://www.ine.es/ss/Satellite?L=es_ES&c=Page&cid=1254735624326&p=1254735624326&pagename=ProductosYServicios%2FPYSLayout)
- URL: `http://www.ine.es/prodyser/callejero/caj_esp/caj_esp_01[YYYY].zip` donde `YYYY` es el año, (desde 2013 hasta la actualidad))
- Tipo: Texto de ancho fijo comprimido (.zip)
- Datos procesados: [/data/codigos_postales_municipios_historical.csv](data/codigos_postales_municipios_historical.csv)
### Formato de los datos
Ejemplo en CSV:
| codigo_postal | municipio_id | nombre_entidad_singular | year |
|---------------|--------------|-------------------------|------|
| 29610 | 29076 | OJEN | 2013 |
| 29610 | 29076 | OJEN | 2014 |
| 29612 | 29076 | OJEN | 2014 |
En el ejemplo se aprecia como en 2014 aparece un nuevo código postal para el municipio de Ojén.
## Script
El script se puede encontrar en [/scripts/ine](/scripts/ine).
## Merge con ds-organizacion-administrativa
Para obtener el nombre de municipio se puede hacer un merge con `ds-organizacion-administrativa/ds-oa-municipios` mediante `csvjoin`:
$ `curl https://raw.githubusercontent.com/codeforspain/ds-organizacion-administrativa/master/data/municipios.csv |
csvcut -c 'municipio_id,nombre' |csvjoin -c "municipio_id" codigos_postales_municipios.csv - |
csvcut -C "municipio_id,nombre_entidad_singular" >codigos_postales_municipios_join.csv`
Este comando devuelve [codigos_postales_municipios_join.csv](data/codigos_postales_municipios_join.csv). Ejemplo:
| codigo_postal | nombre_entidad_singular | municipio_id | nombre |
|---------------|-------------------------|--------------|----------------------------|
| 28100 | ALCOBENDAS | 28006 | Alcobendas |
| 28108 | ALCOBENDAS | 28006 | Alcobendas |
| 28109 | ALCOBENDAS | 28006 | Alcobendas |
| 28110 | DEHESA NUEVA | 28009 | Algete |
| 28120 | PUEBLAS (LAS) | 28045 | Colmenar Viejo |
| 28124 | ROBLEDILLO DE LA JARA | 28124 | Robledillo de la Jara |
| 28130 | MIRAVAL | 28162 | Valdeolmos-Alalpardo |
| 28140 | ADOBERAS | 28059 | Fuente el Saz de Jarama |
| 28150 | MIRADOR (EL) | 28164 | Valdetorres de Jarama |
| 28160 | MORALEJA (LA) | 28145 | Talamanca de Jarama |
| 28170 | COTO DE SAN BENITO | 28163 | Valdepiélagos |
| 28180 | TOMILLARES (LOS) | 28151 | Torrelaguna |
| 28189 | SOTO (EL) | 28153 | Torremocha de Jarama |
| 28190 | PUEBLA DE LA SIERRA | 28118 | Puebla de la Sierra |
| 28191 | PRADENA DEL RINCON | 28117 | Prádena del Rincón |
| 28192 | DEHESA BOYAL | 28021 | "Berrueco, El" |
| 28193 | CERCADOS (LOS) | 28039 | Cervera de Buitrago |
| 28194 | VILLAR (EL) | 28124 | Robledillo de la Jara |
| 28195 | SERRADA DE LA FUENTE | 28902 | Puentes Viejas |
| 28196 | PRESA DE PUENTES VIEJAS | 28902 | Puentes Viejas |
| 28200 | JURISDICCION (LA) | 28131 | San Lorenzo de El Escorial |
| 28210 | VALDEMORILLO | 28160 | Valdemorillo |
| 28211 | PERALEJO | 28054 | "Escorial, El" |
| 28212 | BARRANCOS (LOS) | 28095 | Navalagamella |

BIN
archive/caj_esp_012013.zip Normal file

Binary file not shown.

BIN
archive/caj_esp_012014.zip Normal file

Binary file not shown.

BIN
archive/caj_esp_012015.zip Normal file

Binary file not shown.

BIN
archive/caj_esp_012016.zip Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

146
datapackage.json Normal file
View File

@ -0,0 +1,146 @@
{
"name": "ds-codigos-postales",
"title": "Dataset de Códigos Postales",
"descriptions": "Codigos postales de los municipios españoles",
"licenses": [
{
"type": "odc-pddl",
"url": "http:\/\/opendatacommons.org\/licenses\/pddl\/"
}
],
"author": {
"name": "Code for Spain",
"web": "http:\/\/www.codeforspain.org"
},
"keywords": [
"Codigo Postal"
],
"sources": [
{
"name": "Callejero del Censo Electoral (INE)",
"web": "http:\/\/www.ine.es\/ss\/Satellite?L=es_ES&c=Page&cid=1254735624326&p=1254735624326&pagename=ProductosYServicios%2FPYSLayout"
}
],
"resources": [
{
"name": "ds_codigos_postales_municipios",
"title": "Codigos postales por municipio",
"format": "csv",
"path": "data\/codigos_postales_municipios.csv",
"schema": {
"fields": [
{
"name": "codigo_postal",
"type": "number",
"description": "Código Postal",
"pattern": "[0-9]{5}"
},
{
"name": "municipio_id",
"type": "number",
"description": "Código INE del municipio",
"pattern": "[0-9]{5}"
},
{
"name": "nombre_entidad_singular",
"type": "string",
"description": "Nombre entidad singular"
}
]
}
},
{
"name": "ds_codigos_postales_municipios_historical",
"title": "Histórico de codigos postales por municipio (desde 2012)",
"format": "csv",
"path": "data\/codigos_postales_municipios_historical.csv",
"schema": {
"fields": [
{
"name": "codigo_postal",
"type": "number",
"description": "Código Postal",
"pattern": "[0-9]{5}"
},
{
"name": "municipio_id",
"type": "number",
"description": "Código INE del municipio",
"pattern": "[0-9]{5}"
},
{
"name": "nombre_entidad_singular",
"type": "string",
"description": "Nombre entidad singular"
},
{
"name": "year",
"type": "number",
"description": "Año del dato"
}
]
}
},
{
"name": "ds_codigos_postales_municipios",
"title": "Codigos postales por municipio",
"format": "json",
"path": "data\/codigos_postales_municipios.json",
"schema": {
"fields": [
{
"name": "codigo_postal",
"type": "number",
"description": "Código Postal",
"pattern": "[0-9]{5}"
},
{
"name": "municipio_id",
"type": "number",
"description": "Código INE del municipio",
"pattern": "[0-9]{5}"
},
{
"name": "nombre_entidad_singular",
"type": "string",
"description": "Nombre entidad singular"
}
]
}
},
{
"name": "ds_codigos_postales_municipios_historical",
"title": "Histórico de codigos postales por municipio (desde 2012)",
"format": "json",
"path": "data\/codigos_postales_municipios_historical.json",
"schema": {
"fields": [
{
"name": "codigo_postal",
"type": "number",
"description": "Código Postal",
"pattern": "[0-9]{5}"
},
{
"name": "municipio_id",
"type": "number",
"description": "Código INE del municipio",
"pattern": "[0-9]{5}"
},
{
"name": "nombre_entidad_singular",
"type": "string",
"description": "Nombre entidad singular"
},
{
"name": "year",
"type": "number",
"description": "Año del dato"
}
]
}
}
],
"last_updated": "2016-05-16 ",
"version": "0.0.4"
}

77
scripts/ine/Readme.md Normal file
View File

@ -0,0 +1,77 @@
# Script
Procesa los archivos fuente con los tipos impositivos municipales alojados en `../archive`.
Si no los encuentra, los descarga.
## Modo de Uso
$ php script.php [COMMAND SUBCOMMAND]] [OPTIONS]
Si se invoca sin subcomandos o argumentos, executa:
1. download all
2. process all
3. update
4. convert-to-json
### Opciones
COMMANDS
download [SUBCOMMAND] Descarga los archivos fuente, pero no los procesa.
Se puede especificar opcionalmente un subcomando:
year YEAR Descarga únicamente el año YEAR
all Descarga todos los años
OPTIONS
--force, -f Fuerza la descarga de los archivos fuente, aunque existan
process [SUBCOMMAND] Procesa los archivos fuente, y crea los archivos CSV/JSON.
Se puede especificar opcionalmente un subcomando:
year YEAR Procesa únicamente el año YEAR
year last Procesa únicamente el último año
historical Procesa todos los años y general histórico.
all Procesa todo
convert-to-json Convierte todos los archivos .csv almacenados en /data a .json.
update Actualiza el archivo datapackage.json
## Requisitos
* PHP 5.4+
* csvkit 1.0.0
### csvkit
Para poder generar los archivos en `.json`, hay que tener instalado [csvkit](https://csvkit.readthedocs.org/en/540/index.html). Para este script se ha usado la version 1.0.0.
Se instala mediante:
$ sudo pip install csvkit
Así mismo, `pip` tiene que estar instalado. En Ubuntu esto se hace mediante:
$ sudo apt-get install python-pip python-dev build-essential
$ sudo pip install --upgrade pip
$ sudo pip install --upgrade virtualenv

View File

@ -0,0 +1,6 @@
{
"require":
{
"maximebf/consolekit": ">=1.0.0"
}
}

63
scripts/ine/composer.lock generated Normal file
View File

@ -0,0 +1,63 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "ccc11f85419078d82db868d206e82c03",
"content-hash": "479d298de52b319ceb16085a3f9b74ca",
"packages": [
{
"name": "maximebf/consolekit",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/maximebf/ConsoleKit.git",
"reference": "c46403e2cf98e8413168c523bdbb7c13a386abe5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maximebf/ConsoleKit/zipball/c46403e2cf98e8413168c523bdbb7c13a386abe5",
"reference": "c46403e2cf98e8413168c523bdbb7c13a386abe5",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-0": {
"ConsoleKit": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
}
],
"description": "Library to create command line utilities",
"homepage": "https://github.com/maximebf/ConsoleKit",
"keywords": [
"cli",
"commands",
"console",
"shell"
],
"time": "2013-01-01 16:03:55"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

0
scripts/ine/joe Normal file
View File

104
scripts/ine/lib/Config.php Normal file
View File

@ -0,0 +1,104 @@
<?php
class Config
{
const URL = "http://www.ine.es/prodyser/callejero/caj_esp/caj_esp_01%d.zip";
const YEAR_START = 2013; //existe desde 2012, pero usa doble compresión. To do.
const DATA_FOLDER ="data";
const ARCHIVE_FOLDER = "archive";
const SOURCE_FILE = "caj_esp_01%d.zip";
const DEST_FILE = "codigos_postales_municipios";
const DEST_HISTORICAL_FILE = "codigos_postales_municipios_historical";
//TRAMOS-NAL.F151231
static $datapackage = [
"name" => "ds-codigos-postales",
"title" => "Dataset de Códigos Postales",
"descriptions" => "Codigos postales de los municipios españoles",
"licenses" => [
[
"type" => "odc-pddl",
"url" => "http://opendatacommons.org/licenses/pddl/"
]
],
"author" => [
"name" => "Code for Spain",
"web" => "http://www.codeforspain.org"
],
"keywords" => [ "Codigo Postal"],
"sources" => [
[
"name" => "Callejero del Censo Electoral (INE)",
"web" => "http://www.ine.es/ss/Satellite?L=es_ES&c=Page&cid=1254735624326&p=1254735624326&pagename=ProductosYServicios%2FPYSLayout"
]
],
"resources" => [
[
"name" => "ds_codigos_postales_municipios",
"title"=> "Codigos postales por municipio",
"format"=> "csv",
"path"=> "data/codigos_postales_municipios.csv",
"schema"=> [
"fields"=> [
[
"name" => "codigo_postal",
"type" => "number",
"description" => "Código Postal",
"pattern" => "[0-9]{5}"
],
[
"name" => "municipio_id",
"type" => "number",
"description" => "Código INE del municipio",
"pattern" => "[0-9]{5}"
],
[
"name" => "nombre_entidad_singular",
"type" => "string",
"description" => "Nombre entidad singular",
],
]
]
],
[
"name" => "ds_codigos_postales_municipios_historical",
"title"=> "Histórico de codigos postales por municipio (desde 2012)",
"format"=> "csv",
"path"=> "data/codigos_postales_municipios_historical.csv",
"schema"=> [
"fields"=> [
[
"name" => "codigo_postal",
"type" => "number",
"description" => "Código Postal",
"pattern" => "[0-9]{5}"
],
[
"name" => "municipio_id",
"type" => "number",
"description" => "Código INE del municipio",
"pattern" => "[0-9]{5}"
],
[
"name" => "nombre_entidad_singular",
"type" => "string",
"description" => "Nombre entidad singular",
],
[
"name" => "year",
"type" => "number",
"description" => "Año del dato",
],
]
]
]
]
];
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Si no existen, descarga los archivos fuente y los graba a disco
*
* @arg source fuente a procesar (OPCIONAL). Puede ser "municipios", "provincias", "autonomias" o "islas". Si no se especifica procesa todo.
* @opt force fuerza la descarga de los ficheros fuente aunque ya existan
*
*/
use ConsoleKit\Widgets\ProgressBar;
use ConsoleKit\Widgets\Box;
class ConvertToJsonCommand extends ConsoleKit\Command
{
public function execute(array $args, array $options = array())
{
$box = new ConsoleKit\Widgets\Box($this->getConsole(), 'Convirtiendo a JSON');
$box->write();$this->getConsole()->writeln("");
$files = glob(BASE_PATH . DS . Config::DATA_FOLDER . DS ."*.csv");
$progress = new ProgressBar($this->getConsole(), sizeof($files));
foreach ($files as $csvFile) {
$outputFile = BASE_PATH . DS . Config::DATA_FOLDER . DS . basename($csvFile,".csv") . ".json";
exec("csvjson $csvFile > $outputFile");
$progress->incr();
}
$progress->stop();
}
}

View File

@ -0,0 +1,74 @@
<?php
require_once('Config.php');
/**
* Descarga los archivos fuente y los graba a disco
*
*/
class DownloadCommand extends ConsoleKit\Command
{
/**
* Overriding para que invoque all por defecto.
*/
public function execute(array $args, array $options = array())
{
//si se invoca sin sucomando, ejecuta todo
if (!count($args)) {
$args=['all'];
}
return parent::execute($args, $options);
}
/**
* Descarga todos los años
*
* @opt force fuerza la descarga de los ficheros fuente aunque ya existan
*
*/
public function executeAll(array $args, array $options = array())
{
for ($year = Config::YEAR_START;$year <= date('Y');$year++){
$this->downloadYear($year,$options);
}
}
/**
* Descarga solo el año especificado
*
* @arg year año a descargar
* @opt force fuerza la descarga de los ficheros fuente aunque ya existan
*/
public function executeYear(array $args, array $options = array())
{
if (empty($args[0])) {
$this->writeerr("Error: Year missing\n");
die();
}
$this->downloadYear($args['0'],$options);
}
/**
* Descarga archivo fuente especificado por año
*
* @param year año a descargar
* @opt force fuerza la descarga de los ficheros fuente aunque ya existan
*/
private function downloadYear($year,$options=array())
{
$url=sprintf(Config::URL,$year);
$fileName = sprintf(Config::SOURCE_FILE, $year);
$destFileNameFull = BASE_PATH . DS . Config::ARCHIVE_FOLDER . DS . $fileName;
if (!file_exists($destFileNameFull) || isset($options['force']) || isset($options['f'])){
file_put_contents($destFileNameFull, fopen($url, 'r'));
$box = new ConsoleKit\Widgets\Box($this->getConsole(), "Descargando Año - {$year}");
$box->write();$this->getConsole()->writeln("");
}
}
}

View File

@ -0,0 +1,152 @@
<?php
require_once('Config.php');
use ConsoleKit\Widgets\ProgressBar;
/**
* Procesa los archivos fuente y graba a disco los datos en CSV
*
*/
class ProcessCommand extends ConsoleKit\Command
{
/*
* Overriding para que invoque all por defecto.
*/
public function execute(array $args, array $options = array())
{
//si se invoca sin sucomando, ejecuta todo
if (!count($args)) {
$args=['all'];
}
return parent::execute($args, $options);
}
/*
* Procesa todo
*
*/
public function executeAll(array $args, array $options = array())
{
$this->executeHistorical($args,$options);
$args[0]='last';
$this->executeYear($args,$options);
}
/*
* Genera Histórico
*
*/
public function executeHistorical(array $args, array $options = array())
{
$box = new ConsoleKit\Widgets\Box($this->getConsole(), 'Generando Histórico');
$box->write();$this->getConsole()->writeln("");
$file = fopen(BASE_PATH . DS . Config::DATA_FOLDER . DS . Config::DEST_HISTORICAL_FILE . ".csv", 'w+');
$this->writeHeaderToFile($file,Config::$datapackage['resources']['1']['schema']['fields']);
for ($year = Config::YEAR_START; $year <= date('Y'); $year++) {
$this->parseYearToFile($year,$file);
}
}
/**
* Procesa el archivo fuente correspondiente al año especificado
*
* @opt year año a procesar
*
*/
public function executeYear(array $args, array $options = array()){
if (empty($args[0])) {
$this->writeerr("Error: Year missing\n");
die();
}
$year=($args[0]=='last')?date('Y'):$args[0];
$box = new ConsoleKit\Widgets\Box($this->getConsole(), "Generando Año - {$year}");
$box->write();$this->getConsole()->writeln("");
$file = fopen(BASE_PATH . DS . Config::DATA_FOLDER . DS . Config::DEST_FILE . ".csv", 'w+');
$this->writeHeaderToFile($file,Config::$datapackage['resources']['0']['schema']['fields']);
$this->parseYearToFile($year,$file,false);
}
/*
* Graba el el cabecero con el nombre de las columnas a disco
*
* @param resource $file identificador del archivo
*
*/
private function writeHeaderToFile($file,$columns)
{
fputcsv($file, array_map(function($var){ return $var['name']; }, $columns));
return;
}
/*
* Procesa el archivo fuente del año especificado y lo graba a disco
*
* @param int $year año a procesar
* @param resource $file identificador del archivo
*
*/
private function parseYearToFile($year,$file,$includeYear=true){
$fileName = BASE_PATH . DS . Config::ARCHIVE_FOLDER . DS . sprintf(Config::SOURCE_FILE, $year);
$zip = new ZipArchive;
$zip->open($fileName);
for( $i = 0; $i < $zip->numFiles; $i++ ) {
if (strstr($zip->statIndex($i)['name'], 'TRAMOS-NAL')) {
$zippedSourceFileName = $zip->statIndex($i)['name'];
break;
}
}
if (!isset($zippedSourceFileName)){
$this->writeerr("Error: TRAMOS-NAL source not found in {$fileName}\n");
die();
}
$zippedSource = $zip->getStream($zippedSourceFileName);
while (($line = fgets($zippedSource)) !== false) {
$codigo_postal = substr($line,42,5);
$municipio_id = substr($line,0,5);
$nombre_entidad_singular = iconv("windows-1252", "UTF-8", trim(substr($line,110,25)));
$output[$codigo_postal][$municipio_id] = compact('codigo_postal','municipio_id','nombre_entidad_singular');
if ($includeYear) {
$output[$codigo_postal][$municipio_id]['year'] = $year;
}
$i++;
}
ksort($output);
foreach ($output as $codigos_postales){
foreach ($codigos_postales as $codigo_postal){
fputcsv($file, $codigo_postal);
}
}
}
}

View File

@ -0,0 +1,50 @@
<?php
require_once('Config.php');
use ConsoleKit\Widgets\ProgressBar;
/**
* Actualiza datapackage.json
*
* @opt nojson no incluye los recursos .json
*/
class UpdateCommand extends ConsoleKit\Command
{
public function execute(array $args, array $options = array())
{
$datapackageNew = Config::$datapackage;
$datapackageNew['last_updated']=date('Y-m-d ');
$box = new ConsoleKit\Widgets\Box($this->getConsole(), 'Actualizando datapackage.json');
$box->write();$this->getConsole()->writeln("");
// Comprobobamos si hay que omitir JSON
if (!isset($options['nojson']) && !isset($options['n'])){
foreach ( Config::$datapackage['resources'] as $resource){
$resource['format'] = 'json';
$resource['path'] = array_shift(explode('.',$resource['path'])) . ".json";
$datapackageNew['resources'][]=$resource;
}
}
// Actualizamos versión
if (file_exists(BASE_PATH . DS . "datapackage.json")) {
$datapackageOld = json_decode(file_get_contents(BASE_PATH . DS . "datapackage.json"));
if (!empty($datapackageOld->version)) {
$semver = explode(".", $datapackageOld->version);
if (sizeof($semver)==3) $datapackageNew['version'] = implode(".", [$semver[0],$semver[1],++$semver[2]]);
} else {
$datapackageNew['version']="0.0.1";
}
}
file_put_contents(BASE_PATH . DS . "datapackage.json", json_encode($datapackageNew,JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
}

27
scripts/ine/script.php Executable file
View File

@ -0,0 +1,27 @@
<?php
require_once "vendor/autoload.php";
require_once('lib/Config.php');
define('DS','/');
define ('BASE_PATH', dirname(__DIR__) . DS . "..");
// Comprobamos si los directorios están creados. Si no lo están, los creamos
is_dir(BASE_PATH . DS . Config::DATA_FOLDER) || mkdir(BASE_PATH . DS . Config::DATA_FOLDER);
is_dir(BASE_PATH . DS . Config::ARCHIVE_FOLDER) || mkdir(BASE_PATH . DS . Config::ARCHIVE_FOLDER);
// Inicializamos consola
$console = new ConsoleKit\Console();
$console->addCommandsFromDir("lib",null,true);
if (sizeof($argv)==1) {
$console->run(['download']);
$console->run(['process']);
$console->run(['convert-to-json']);
$console->run(['update']);
} else {
$console->run();
}

7
scripts/ine/vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit9ac7a9a9b7ce69de787812e3f7bc01d1::getLoader();

View File

@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
scripts/ine/vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) 2015 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ConsoleKit' => array($vendorDir . '/maximebf/consolekit/src'),
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,45 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit9ac7a9a9b7ce69de787812e3f7bc01d1
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit9ac7a9a9b7ce69de787812e3f7bc01d1', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit9ac7a9a9b7ce69de787812e3f7bc01d1', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
$loader->register(true);
return $loader;
}
}

View File

@ -0,0 +1,48 @@
[
{
"name": "maximebf/consolekit",
"version": "1.0.3",
"version_normalized": "1.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/maximebf/ConsoleKit.git",
"reference": "c46403e2cf98e8413168c523bdbb7c13a386abe5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maximebf/ConsoleKit/zipball/c46403e2cf98e8413168c523bdbb7c13a386abe5",
"reference": "c46403e2cf98e8413168c523bdbb7c13a386abe5",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2013-01-01 16:03:55",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"ConsoleKit": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
}
],
"description": "Library to create command line utilities",
"homepage": "https://github.com/maximebf/ConsoleKit",
"keywords": [
"cli",
"commands",
"console",
"shell"
]
}
]

View File

@ -0,0 +1,5 @@
language: php
php:
- 5.3
- 5.4
script: phpunit

View File

@ -0,0 +1,19 @@
Copyright (C) 2012 Maxime Bouroumeau-Fuseau
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,203 @@
# ConsoleKit
PHP 5.3+ library to create command line utilities.
[![Build Status](https://secure.travis-ci.org/maximebf/ConsoleKit.png)](http://travis-ci.org/maximebf/ConsoleKit)
## Example
In *cli.php*:
<?php
class HelloCommand extends ConsoleKit\Command
{
public function execute(array $args, array $options = array())
{
$this->writeln('hello world!', ConsoleKit\Colors::GREEN);
}
}
$console = new ConsoleKit\Console();
$console->addCommand('HelloCommand');
$console->run();
In the shell:
$ php cli.php hello
hello world!
More examples in [example.php](https://github.com/maximebf/ConsoleKit/blob/master/example.php)
## Installation
The easiest way to install ConsoleKit is using [Composer](https://github.com/composer/composer)
with the following requirement:
{
"require": {
"maximebf/consolekit": ">=1.0.0"
}
}
Alternatively, you can [download the archive](https://github.com/maximebf/ConsoleKit/zipball/master)
and add the src/ folder to PHP's include path:
set_include_path('/path/to/src' . PATH_SEPARATOR . get_include_path());
ConsoleKit does not provide an autoloader but follows the [PSR-0 convention](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md).
You can use the following snippet to autoload ConsoleKit classes:
spl_autoload_register(function($className) {
if (substr($className, 0, 10) === 'ConsoleKit') {
$filename = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\')) . '.php';
require_once $filename;
}
});
## Usage
### Options parser
The default options parser parses an argv-like array.
Items can be of the form:
- --key=value
- --key
- -a
- -ab (equivalent of -a -b)
When an option has no value, true will be used. If multiple key/value pairs
with the same key are specified, the "key" value will be an array containing all the values.
If "--" is detected, all folowing values will be treated as a single argument
Example: the string "-a -bc --longopt --key=value arg1 arg2 -- --any text" will produce the following two arrays:
$args = array('arg1', 'arg2', '--any text');
$options = array('a' => true, 'b' => true, 'c' => true, 'longopt' => true, 'key' => 'value');
### Creating commands
Any callbacks can be a command. It will receive three parameters: the
arguments array, the options array and the console object.
function my_command($args, $opts, $console) {
$console->writeln("hello world!");
}
Commands can also be defined as classes. In this case, they must inherit from `ConsoleKit\Command`
and override the `execute()` method.
class MyCommand extends ConsoleKit\Command {
public function execute(array $args, array $opts) {
$this->writeln("hello world!");
}
}
The `ConsoleKit\Command` class offers helper methods, check it out for more info.
### Registering commands
Commands need to be registered in the console object using the `addCommand()` method (or `addCommands()`).
$console = new ConsoleKit\Console();
$console->addCommand('my_command'); // the my_command function
$console->addCommand('MyCommand'); // the MyCommand class
$console->addCommand(function() { echo 'hello!'; }, 'hello'); // using a closure
// or:
$console->addCommand('hello', function() { echo 'hello!'; }); // alternative when using a closure
Notice that in the last example we have provided a second argument which is an alias for a command.
As closures have no name, one must be specified.
The command name for functions is the same as the function name with underscores replaced
by dashes (ie. my\_command becomes my-command).
The command name for command classes is the short class name without the `Command`
suffix and "dashized" (ie. HelloWorldCommand becomes hello-world).
### Running
Simply call the `run()` method of the console object
$console->run();
$console->run(array('custom arg1', 'custom arg2')); // overrides $_SERVER['argv']
### Automatic help generation
The *help* command is automatically registered and provides help about available methods based on doc comments.
Check out [example.php](https://github.com/maximebf/ConsoleKit/blob/master/example.php) for example of available tags
$ php myscript.php help
## Formating text
### Colors
The `ConsoleKit\Colors::colorize()` method provides an easy way to colorize a text.
Colors are defined as either a string or an integer (through constants of the `Colors` class).
Available colors: black, red, green, yellow, blue, magenta, cyan, white.
Foreground colors are also available in a "bold" variant. Suffix the color name with "+bold" or use the OR bit operator with constants.
echo Colors::colorize('my red text', Colors::RED);
echo Colors::colorize('my red text', 'red');
echo Colors::colorize('my red bold text', Colors::RED | Colors::BOLD);
echo Colors::colorize('my red bold text', 'red+bold');
echo Colors::colorize('my red text over yellow background', Colors::RED, Colors::YELLOW);
### TextFormater
The `ConsoleKit\TextFormater` class allows you to format text using the following options:
- indentation using `setIndent()` or the *indent* option
- quoting using `setQuote()` or the *quote* option
- foreground color using `setFgColor()` or the *fgcolor* option
- background color using `setBgColor()` or the *bgcolor* option
Options can be defined using `setOptions()` or as the first parameter of the constructor.
$formater = new ConsoleKit\TextFormater(array('quote' => ' > '));
echo $formater->format("hello!");
// produces: " > hello"
## Widgets
### Dialog
Used to interact with the user
$dialog = new ConsoleKit\Widgets\Dialog($console);
$name = $dialog->ask('What is your name?');
if ($dialog->confirm('Are you sure?')) {
$console->writeln("hello $name");
}
### Box
Wraps text in a box
$box = new ConsoleKit\Widgets\Box($console, 'my text');
$box->write();
Produces:
********************************************
* my text *
********************************************
### Progress bar
Displays a progress bar
$total = 100;
$progress = new ConsoleKit\Widgets\ProgressBar($console, $total);
for ($i = 0; $i < $total; $i++) {
$progress->incr();
usleep(10000);
}
$progress->stop();

View File

@ -0,0 +1,19 @@
{
"name": "maximebf/consolekit",
"description": "Library to create command line utilities",
"keywords": ["console", "shell", "cli", "commands"],
"homepage": "https://github.com/maximebf/ConsoleKit",
"type": "library",
"license": "MIT",
"authors": [{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
}],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-0": {"ConsoleKit": "src/"}
}
}

View File

@ -0,0 +1,97 @@
<?php
include __DIR__ . '/tests/bootstrap.php';
use ConsoleKit\Console,
ConsoleKit\Command,
ConsoleKit\Colors,
ConsoleKit\Utils,
ConsoleKit\Widgets\Dialog,
ConsoleKit\Widgets\ProgressBar;
/**
* Prints hello world
*/
class HelloWorldCommand extends Command
{
public function execute(array $args, array $options = array())
{
$this->writeln('hello world!', Colors::GREEN);
}
}
/**
* Says hello to someone
*
* @arg name The name of the person to say hello to
* @opt color The color in which to print the text
*/
class SayHelloCommand extends Command
{
public function execute(array $args, array $options = array())
{
$this->context(array('fgcolor' => Utils::get($options, 'color')), function($c) use ($args) {
$c->writeln(sprintf('hello %s!', $args[0]));
});
}
}
/**
* Commands to say something to someone!
*/
class SayCommand extends Command
{
/**
* Says hello to someone
*
* @arg name The name of the person to say hello to
*/
public function executeHello(array $args, array $options = array())
{
$name = 'unknown';
if (empty($args)) {
$dialog = new Dialog($this->console);
$name = $dialog->ask('What is your name?', $name);
} else {
$name = $args[0];
}
$this->writeln(sprintf('hello %s!', $name));
}
/**
* Says hi to someone
*
* @arg name The name of the person to say hello to
*/
public function executeHi(array $args, array $options = array())
{
$this->writeln(sprintf('hi %s!', $args[0]));
}
}
/**
* Displays a progress bar
*
* @opt total Number of iterations
* @opt usleep Waiting time in microsecond between each iteration
*/
function progress($args, $options, $console)
{
$total = isset($options['total']) ? $options['total'] : 100;
$usleep = isset($options['usleep']) ? $options['usleep'] : 10000;
$progress = new ProgressBar($console, $total);
for ($i = 0; $i < $total; $i++) {
$progress->incr();
usleep($usleep);
}
$progress->stop();
}
$console = new Console(array(
'hello' => 'HelloWorldCommand',
'SayHelloCommand',
'SayCommand',
'progress'
));
$console->run();

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="ConsoleKit Test Suite">
<directory>./tests/ConsoleKit/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src/ConsoleKit/</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,204 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* Functions to colorize text
*
* Text can be colorized (foreground only) by using a static method named after the color code.
*
* <code>
* $text = Colors::colorize('hello world', Colors::RED)
* $text = Colors::colorize('hello world', 'red')
* $text = Colors::colorize('hello world', Colors::RED | Colors::BOLD)
* $text = Colors::colorize('hello world', 'red+bold')
* $text = Colors::red('hello world');
* </code>
*/
class Colors
{
const RESET = "\033[0m";
const BLACK = 1;
const RED = 2;
const GREEN = 4;
const YELLOW = 8;
const BLUE = 16;
const MAGENTA = 32;
const CYAN = 64;
const WHITE = 128;
const BOLD = 256;
const UNDERSCORE = 512;
const BLINK = 1024;
const REVERSE = 2048;
const CONCEAL = 4096;
/** @var array */
private static $colors = array(
'black' => self::BLACK,
'red' => self::RED,
'green' => self::GREEN,
'yellow' => self::YELLOW,
'blue' => self::BLUE,
'magenta' => self::MAGENTA,
'cyan' => self::CYAN,
'white' => self::WHITE
);
/** @var array */
private static $options = array(
'bold' => self::BOLD,
'underscore' => self::UNDERSCORE,
'blink' => self::BLINK,
'reverse' => self::REVERSE,
'conceal' => self::CONCEAL
);
/** @var array */
private static $codes = array(
self::BLACK => 0,
self::RED => 1,
self::GREEN => 2,
self::YELLOW => 3,
self::BLUE => 4,
self::MAGENTA => 5,
self::CYAN => 6,
self::WHITE => 7,
self::BOLD => 1,
self::UNDERSCORE => 4,
self::BLINK => 5,
self::REVERSE => 7,
self::CONCEAL => 8
);
/**
* Returns a colorized string
*
* @param string $text
* @param string $fgcolor (a key from the $foregroundColors array)
* @param string $bgcolor (a key from the $backgroundColors array)
* @return string
*/
public static function colorize($text, $fgcolor = null, $bgcolor = null)
{
$colors = '';
if ($bgcolor) {
$colors .= self::getBgColorString(self::getColorCode($bgcolor));
}
if ($fgcolor) {
$colors .= self::getFgColorString(self::getColorCode($fgcolor));
}
if ($colors) {
$text = $colors . $text . self::RESET;
}
return $text;
}
/**
* Returns a text with each lines colorized independently
*
* @param string $text
* @param string $fgcolor
* @param string $bgcolor
* @return string
*/
public static function colorizeLines($text, $fgcolor = null, $bgcolor = null)
{
$lines = explode("\n", $text);
foreach ($lines as &$line) {
$line = self::colorize($line, $fgcolor, $bgcolor);
}
return implode("\n", $lines);
}
/**
* Returns a color code
*
* $color can be a string with the color name, or one of the color constants.
*
* @param int|string $color
* @param array $options
* @return int
*/
public static function getColorCode($color, $options = array())
{
$code = (int) $color;
if (is_string($color)) {
$options = array_merge(explode('+', strtolower($color)), $options);
$color = array_shift($options);
if (!isset(self::$colors[$color])) {
throw new ConsoleException("Unknown color '$color'");
}
$code = self::$colors[$color];
}
foreach ($options as $opt) {
$opt = strtolower($opt);
if (!isset(self::$options[$opt])) {
throw new ConsoleException("Unknown option '$color'");
}
$code = $code | self::$options[$opt];
}
return $code;
}
/**
* Returns a foreground color string
*
* @param int $color
* @return string
*/
public static function getFgColorString($colorCode)
{
list($color, $options) = self::extractColorAndOptions($colorCode);
$codes = array_filter(array_merge($options, array("3{$color}")));
return sprintf("\033[%sm", implode(';', $codes));
}
/**
* Returns a background color string
*
* @param int $color
* @return string
*/
public static function getBgColorString($colorCode)
{
list($color, $options) = self::extractColorAndOptions($colorCode);
$codes = array_filter(array_merge($options, array("4{$color}")));
return sprintf("\033[%sm", implode(';', $codes));
}
/**
* Extracts the options and the color from a color code
*
* @param int $colorCode
* @return array
*/
private static function extractColorAndOptions($colorCode)
{
$options = array();
foreach (self::$options as $name => $bit) {
if (($colorCode & $bit) === $bit) {
$options[] = self::$codes[$bit];
$colorCode = $colorCode & ~$bit;
}
}
if (!isset(self::$codes[$colorCode])) {
throw new ConsoleException("Cannot parse color code");
}
return array(self::$codes[$colorCode], $options);
}
public static function __callStatic($method, $args)
{
return self::colorize($args[0], $method);
}
}

View File

@ -0,0 +1,171 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
use Closure,
ReflectionMethod;
/**
* Base class for commands
*
* Can be used to represent a single command:
*
* <code>
* class MyCommand extends Command {
* public function execute(array $args, array $options = array()) {
* $this->writeln('hello world');
* }
* }
* </code>
*
* If "execute()" is not overriden, the execution will be forwarded to subcommand
* methods. The subcommand name will be the first argument value and the associated
* method must be prefixed with "execute".
*
* <code>
* class MyCommand extends Command {
* public function executeSub1(array $args, array $options = array()) {
* $this->writeln('hello world');
* }
* public function executeSub2(array $args, array $options = array()) {
* $this->writeln('hello world');
* }
* }
* </code>
*
*/
abstract class Command
{
/** @var Console */
protected $console;
/** @var array */
protected $defaultFormatOptions = array();
/**
* @param Console $console
*/
public function __construct(Console $console)
{
$this->console = $console;
}
/**
* @return Console
*/
public function getConsole()
{
return $this->console;
}
/**
* If not overriden, will execute the command specified
* as the first argument
*
* Commands must be defined as methods named after the
* command, prefixed with execute (eg. create -> executeCreate)
*
* @param array $args
* @param array $options
*/
public function execute(array $args, array $options = array())
{
if (!count($args)) {
throw new ConsoleException("Missing subcommand name");
}
$command = ucfirst(Utils::camelize(array_shift($args)));
$methodName = "execute$command";
if (!method_exists($this, $methodName)) {
throw new ConsoleException("Command '$command' does not exist");
}
$method = new ReflectionMethod($this, $methodName);
$params = Utils::computeFuncParams($method, $args, $options);
return $method->invokeArgs($this, $params);
}
/**
* Formats text using a {@see TextFormater}
*
* @param string $text
* @param int|array $formatOptions Either an array of options for TextFormater or a color code
* @return string
*/
public function format($text, $formatOptions = array())
{
if (!is_array($formatOptions)) {
$formatOptions = array('fgcolor' => $formatOptions);
}
$formatOptions = array_merge($this->defaultFormatOptions, $formatOptions);
$formater = new TextFormater($formatOptions);
return $formater->format($text);
}
/**
* Executes the closure with a {@see FormatedWriter} object as the first
* argument, initialized with the $formatOptions array
*
* <code>
* $this->context(array('quote' => ' * '), function($f) {
* $f->writeln('quoted text');
* })
* </code>
*
* @param array $formatOptions
* @param Closure $closure
*/
public function context(array $formatOptions, Closure $closure)
{
$formater = new FormatedWriter($this->console, $formatOptions);
return $closure($formater);
}
/**
* Writes some text to the text writer
*
* @see format()
* @param string $text
* @param int|array $formatOptions
* @param int $pipe
* @return Command
*/
public function write($text, $formatOptions = array(), $pipe = TextWriter::STDOUT)
{
$this->console->write($this->format($text, $formatOptions), $pipe);
return $this;
}
/**
* Writes a message in bold red to STDERR
*
* @param string $text
* @return Command
*/
public function writeerr($text)
{
return $this->write($text, Colors::RED | Colors::BOLD, TextWriter::STDERR);
}
/**
* Writes a line of text
*
* @param string $text
* @param int|array $formatOptions
* @param int $pipe
* @return Command
*/
public function writeln($text, $formatOptions = array(), $pipe = TextWriter::STDOUT)
{
$this->console->writeln($this->format($text, $formatOptions), $pipe);
return $this;
}
}

View File

@ -0,0 +1,418 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
use Closure,
DirectoryIterator,
ReflectionFunction,
ReflectionMethod;
/**
* Registry of available commands and command runner
*/
class Console implements TextWriter
{
/** @var OptionsParser */
protected $optionsParser;
/** @var TextWriter */
protected $textWriter;
/** @var bool */
protected $exitOnException = true;
/** @var string */
protected $helpCommand = 'help';
/** @var string */
protected $helpCommandClass = 'ConsoleKit\HelpCommand';
/** @var array */
protected $commands = array();
/** @var string */
protected $defaultCommand;
/** @var bool */
protected $verboseException = false;
/** @var bool */
protected $singleCommand = false;
/**
* @param array $commands
*/
public function __construct(array $commands = array(), OptionsParser $parser = null, TextWriter $writer = null)
{
$this->optionsParser = $parser ?: new DefaultOptionsParser();
$this->textWriter = $writer ?: new StdTextWriter();
if ($this->helpCommandClass) {
$this->addCommand($this->helpCommandClass, $this->helpCommand);
$this->addCommands($commands);
}
}
/**
* @param OptionsParser $parser
* @return Console
*/
public function setOptionsParser(OptionsParser $parser)
{
$this->optionsParser = $parser;
return $this;
}
/**
* @return OptionsParser
*/
public function getOptionsParser()
{
return $this->optionsParser;
}
/**
* @param TextWriter $writer
* @return Console
*/
public function setTextWriter(TextWriter $writer)
{
$this->textWriter = $writer;
return $this;
}
/**
* @return TextWriter
*/
public function getTextWriter()
{
return $this->textWriter;
}
/**
* Sets whether to call exit(1) when an exception is caught
*
* @param bool $exit
* @return Console
*/
public function setExitOnException($exit = true)
{
$this->exitOnException = $exit;
return $this;
}
/**
* @return bool
*/
public function exitsOnException()
{
return $this->exitOnException;
}
/**
* Sets whether a detailed error message is displayed when exception are caught
*
* @param boolean $enable
*/
public function setVerboseException($enable = true)
{
$this->verboseException = $enable;
}
/**
* @return bool
*/
public function areExceptionsVerbose()
{
return $this->verboseException;
}
/**
* Adds multiple commands at once
*
* @see addCommand()
* @param array $commands
* @return Console
*/
public function addCommands(array $commands)
{
foreach ($commands as $name => $command) {
$this->addCommand($command, is_numeric($name) ? null : $name);
}
return $this;
}
/**
* Registers a command
*
* @param callback $callback Associated class name, function name, Command instance or closure
* @param string $alias Command name to be used in the shell
* @param bool $default True to set the command as the default one
* @return Console
*/
public function addCommand($callback, $alias = null, $default = false)
{
if ($alias instanceof \Closure && is_string($callback)) {
list($alias, $callback) = array($callback, $alias);
}
if (is_array($callback) && is_string($callback[0])) {
$callback = implode('::', $callback);
}
$name = '';
if (is_string($callback)) {
$name = $callback;
if (is_callable($callback)) {
if (strpos($callback, '::') !== false) {
list($classname, $methodname) = explode('::', $callback);
$name = Utils::dashized($methodname);
} else {
$name = strtolower(trim(str_replace('_', '-', $name), '-'));
}
} else {
if (substr($name, -7) === 'Command') {
$name = substr($name, 0, -7);
}
$name = Utils::dashized(basename(str_replace('\\', '/', $name)));
}
} else if (is_object($callback) && !($callback instanceof Closure)) {
$classname = get_class($callback);
if (!($callback instanceof Command)) {
throw new ConsoleException("'$classname' must inherit from 'ConsoleKit\Command'");
}
if (substr($classname, -7) === 'Command') {
$classname = substr($classname, 0, -7);
}
$name = Utils::dashized(basename(str_replace('\\', '/', $classname)));
} else if (!$alias) {
throw new ConsoleException("Commands using closures must have an alias");
}
$name = $alias ?: $name;
$this->commands[$name] = $callback;
if ($default) {
$this->defaultCommand = $name;
}
return $this;
}
/**
* Registers commands from a directory
*
* @param string $dir
* @param string $namespace
* @param bool $includeFiles
* @return Console
*/
public function addCommandsFromDir($dir, $namespace = '', $includeFiles = false)
{
foreach (new DirectoryIterator($dir) as $file) {
$filename = $file->getFilename();
if ($file->isDir() || substr($filename, 0, 1) === '.' || strlen($filename) <= 11
|| strtolower(substr($filename, -11)) !== 'command.php') {
continue;
}
if ($includeFiles) {
include $file->getPathname();
}
$className = trim($namespace . '\\' . substr($filename, 0, -4), '\\');
$this->addCommand($className);
}
return $this;
}
/**
* @param string $name
* @return bool
*/
public function hasCommand($name)
{
return isset($this->commands[$name]);
}
/**
* @param string $name
* @return string
*/
public function getCommand($name)
{
if (!isset($this->commands[$name])) {
throw new ConsoleException("Command '$name' does not exist");
}
return $this->commands[$name];
}
/**
* @return array
*/
public function getCommands()
{
return $this->commands;
}
/**
* @param string $name
* @return Console
*/
public function setDefaultCommand($name = null)
{
if ($name !== null && !isset($this->commands[$name])) {
throw new ConsoleException("Command '$name' does not exist");
}
$this->defaultCommand = $name;
}
/**
* @return string
*/
public function getDefaultCommand()
{
return $this->defaultCommand;
}
/**
* Turn off command parsing
*
* @param type $singleCommand
* @return Console
*/
public function setSingleCommand($singleCommand) {
$this->singleCommand = $singleCommand;
return $this;
}
/**
* @param array $args
* @return mixed Results of the command callback
*/
public function run(array $argv = null)
{
try {
if ($argv === null) {
$argv = isset($_SERVER['argv']) ? array_slice($_SERVER['argv'], 1) : array();
}
list($args, $options) = $this->getOptionsParser()->parse($argv);
if($this->defaultCommand && $this->singleCommand) {
return $this->execute($this->defaultCommand, $args, $options);
}
if (!count($args)) {
if ($this->defaultCommand) {
$args[] = $this->defaultCommand;
} else {
$this->textWriter->writeln(Colors::red("Missing command name"));
$args[] = $this->helpCommand;
}
}
$command = array_shift($args);
return $this->execute($command, $args, $options);
} catch (\Exception $e) {
$this->writeException($e);
if ($this->exitOnException) {
exit(1);
}
throw $e;
}
}
/**
* Executes a command
*
* @param string $command
* @param array $args
* @param array $options
* @return mixed
*/
public function execute($command = null, array $args = array(), array $options = array())
{
$command = $command ?: $this->defaultCommand;
if (!isset($this->commands[$command])) {
throw new ConsoleException("Command '$command' does not exist");
}
$callback = $this->commands[$command];
if (is_callable($callback)) {
$params = array($args, $options);
if (is_string($callback)) {
if (strpos($callback, '::') !== false) {
list($classname, $methodname) = explode('::', $callback);
$reflection = new ReflectionMethod($classname, $methodname);
} else {
$reflection = new ReflectionFunction($callback);
}
$params = Utils::computeFuncParams($reflection, $args, $options);
}
$params[] = $this;
return call_user_func_array($callback, $params);
}
$method = new ReflectionMethod($callback, 'execute');
$params = Utils::computeFuncParams($method, $args, $options);
return $method->invokeArgs(new $callback($this), $params);
}
/**
* Writes some text to the text writer
*
* @see TextWriter::write()
* @param string $text
* @param array $formatOptions
* @return Console
*/
public function write($text, $pipe = TextWriter::STDOUT)
{
$this->textWriter->write($text, $pipe);
return $this;
}
/**
* Writes a line of text
*
* @see TextWriter::writeln()
* @param string $text
* @param array $formatOptions
* @return Console
*/
public function writeln($text = '', $pipe = TextWriter::STDOUT)
{
$this->textWriter->writeln($text, $pipe);
return $this;
}
/**
* Writes an error message to stderr
*
* @param \Exception $e
* @return Console
*/
public function writeException(\Exception $e)
{
if ($this->verboseException) {
$text = sprintf("[%s]\n%s\nIn %s at line %s\n%s",
get_class($e),
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
);
} else {
$text = sprintf("\n[%s]\n%s\n", get_class($e), $e->getMessage());
}
$box = new Widgets\Box($this->textWriter, $text, '');
$out = Colors::colorizeLines($box, Colors::WHITE, Colors::RED);
$out = TextFormater::apply($out, array('indent' => 2));
$this->textWriter->writeln($out);
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
class ConsoleException extends \Exception
{
}

View File

@ -0,0 +1,72 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* Default parser for the $_SERVER['argv'] array
*
* Options can be of the form:
* --key=value
* --key
* -a
* -ab (equivalent of -a -b)
*
* When an option has no value, true will be used. If multiple key/value pairs
* with the same key are specified, the "key" value will be an array containing all the values.
* If "--" is detected, all folowing values will be treated as a single argument
*
*/
class DefaultOptionsParser implements OptionsParser
{
/**
* Parses the array and returns a tuple containing the arguments and the options
*
* @param array $argv
* @return array
*/
public function parse(array $argv)
{
$args = array();
$options = array();
for ($i = 0, $c = count($argv); $i < $c; $i++) {
$arg = $argv[$i];
if ($arg === '--') {
$args[] = implode(' ', array_slice($argv, $i + 1));
break;
}
if (substr($arg, 0, 2) === '--') {
$key = substr($arg, 2);
$value = true;
if (($sep = strpos($arg, '=')) !== false) {
$key = substr($arg, 2, $sep - 2);
$value = substr($arg, $sep + 1);
}
if (array_key_exists($key, $options)) {
if (!is_array($options[$key])) {
$options[$key] = array($options[$key]);
}
$options[$key][] = $value;
} else {
$options[$key] = $value;
}
} else if (substr($arg, 0, 1) === '-') {
foreach (str_split(substr($arg, 1)) as $key) {
$options[$key] = true;
}
} else {
$args[] = $arg;
}
}
return array($args, $options);
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* Simple writer that prints text using php's "echo()" function
*
* Note: Pipes are ignored
*/
class EchoTextWriter implements TextWriter
{
public function write($text, $pipe = TextWriter::STDOUT)
{
echo $text;
}
public function writeln($text = '', $pipe = TextWriter::STDOUT)
{
$this->write("$text\n", $pipe);
}
}

View File

@ -0,0 +1,46 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* Utility functions to create files
*/
class FileSystem
{
public static function touch($filename, $content = '')
{
if (!self::mkdir(dirname($filename))) {
return false;
}
return file_put_contents($filename, $content) !== false;
}
public static function copyFile($src, $dest)
{
if (!self::mkdir(dirname($dest))) {
return false;
}
return copy($src, $dest);
}
public static function mkdir($dir)
{
if (file_exists($dir)) {
return is_dir($dir);
}
return (bool) @mkdir($dir, 0777, true);
}
public static function join($p1, $p2)
{
return implode(DIRECTORY_SEPARATOR, func_get_args());
}
}

View File

@ -0,0 +1,76 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* A TextWriter proxy which formats text before writing it
*/
class FormatedWriter extends TextFormater implements TextWriter
{
/** @var TextWriter */
protected $textWriter;
/**
* @param TextWriter $writer
* @param array $formatOptions
*/
public function __construct(TextWriter $writer, array $formatOptions = array())
{
parent::__construct($formatOptions);
$this->textWriter = $writer;
}
/**
* @param TextWriter $writer
* @return FormatedWriter
*/
public function setTextWriter(TextWriter $writer)
{
$this->textWriter = $writer;
return $this;
}
/**
* @return TextWriter
*/
public function getTextWriter()
{
return $this->textWriter;
}
/**
* Writes some text to the text writer
*
* @see TextWriter::write()
* @param string $text
* @param array $formatOptions
* @return Command
*/
public function write($text, $pipe = TextWriter::STDOUT)
{
$this->textWriter->write($this->format($text), $pipe);
return $this;
}
/**
* Writes a line of text
*
* @see TextWriter::writeln()
* @param string $text
* @param array $formatOptions
* @return Command
*/
public function writeln($text = '', $pipe = TextWriter::STDOUT)
{
$this->textWriter->writeln($this->format($text), $pipe);
return $this;
}
}

View File

@ -0,0 +1,236 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
use ReflectionClass,
ReflectionFunction;
/**
* Generates help messages based on information in doc comments
*/
class Help
{
/** @var string */
protected $description = '';
/** @var string */
protected $usage = '';
/** @var array */
protected $args = array();
/** @var array */
protected $options = array();
/** @var array */
protected $flags = array();
/** @var array */
protected $subCommands = array();
/**
* Creates an Help object from a FQDN
*
* @param string $fqdn
* @param string $subCommand
* @return Help
*/
public static function fromFQDN($fqdn, $subCommand = null)
{
if (function_exists($fqdn)) {
return self::fromFunction($fqdn);
}
if (class_exists($fqdn) && is_subclass_of($fqdn, 'ConsoleKit\Command')) {
return self::fromCommandClass($fqdn, $subCommand);
}
throw new ConsoleException("'$fqdn' is not a valid ConsoleKit FQDN");
}
/**
* Creates an Help object from a function
*
* @param string $name
* @return Help
*/
public static function fromFunction($name)
{
$func = new ReflectionFunction($name);
return new Help($func->getDocComment());
}
/**
* Creates an Help object from a class subclassing Command
*
* @param string $name
* @param string $subCommand
* @return Help
*/
public static function fromCommandClass($name, $subCommand = null)
{
$prefix = 'execute';
$class = new ReflectionClass($name);
if ($subCommand) {
$method = $prefix . ucfirst(Utils::camelize($subCommand));
if (!$class->hasMethod($method)) {
throw new ConsoleException("Sub command '$subCommand' of '$name' does not exist");
}
return new Help($class->getMethod($method)->getDocComment());
}
$help = new Help($class->getDocComment());
foreach ($class->getMethods() as $method) {
if (strlen($method->getName()) > strlen($prefix) &&
substr($method->getName(), 0, strlen($prefix)) === $prefix) {
$help->subCommands[] = Utils::dashized(substr($method->getName(), strlen($prefix)));
}
}
return $help;
}
/**
* @param string $text
*/
protected function __construct($text = '')
{
$this->text = $text;
$this->parse();
}
protected function parse()
{
$this->usage = '';
$this->args = array();
$this->options = array();
$this->flags = array();
$lines = explode("\n", substr(trim($this->text), 2, -2));
$lines = array_map(function($v) { return ltrim(trim($v), '* '); }, $lines);
$desc = array();
foreach ($lines as $line) {
if (preg_match('/@usage (.+)$/', $line, $matches)) {
$this->usage = $matches[1];
} else if (preg_match('/@arg ([^\s]+)( (.*)|)$/', $line, $matches)) {
$this->args[$matches[1]] = isset($matches[3]) ? $matches[3] : '';
} else if (preg_match('/@opt ([a-zA-Z\-_0-9=]+)( (.*)|)$/', $line, $matches)) {
$this->options[$matches[1]] = isset($matches[3]) ? $matches[3] : '';
} else if (preg_match('/@flag ([a-zA-Z0-9])( (.*)|)$/', $line, $matches)) {
$this->flags[$matches[1]] = isset($matches[3]) ? $matches[3] : '';
} else if (!preg_match('/^@([a-zA-Z\-_0-9]+)(.*)$/', $line)) {
$desc[] = $line;
}
}
$this->description = trim(implode("\n", $desc), "\n ");
}
/**
* @return string
*/
public function getDescrition()
{
return $this->description;
}
/**
* @return string
*/
public function getUsage()
{
return $this->usage;
}
/**
* @return array
*/
public function getArgs()
{
return $this->args;
}
/**
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* @return array
*/
public function getFlags()
{
return $this->flags;
}
/**
* @return bool
*/
public function hasSubCommands()
{
return !empty($this->subCommands);
}
/**
* @return array
*/
public function getSubCommands()
{
return $this->subCommands;
}
/**
* @return string
*/
public function render()
{
$output = "{$this->description}\n\n";
if (!empty($this->usage)) {
$output .= "Usage: {$this->usage}\n\n";
}
if (!empty($this->args)) {
$output .= Colors::colorize("Arguments:\n", Colors::BLACK | Colors::BOLD);
foreach ($this->args as $name => $desc) {
$output .= sprintf(" %s\t%s\n", $name, $desc);
}
$output .= "\n";
}
if (!empty($this->options)) {
$output .= Colors::colorize("Available options:\n", Colors::BLACK | Colors::BOLD);
foreach ($this->options as $name => $desc) {
$output .= sprintf(" --%s\t%s\n", $name, $desc);
}
$output .= "\n";
}
if (!empty($this->flags)) {
$output .= Colors::colorize("Available flags:\n", Colors::BLACK | Colors::BOLD);
foreach ($this->flags as $name => $desc) {
$output .= sprintf(" -%s\t%s\n", $name, $desc);
}
$output .= "\n";
}
if (!empty($this->subCommands)) {
$output .= Colors::colorize("Available sub commands:\n", Colors::BLACK | Colors::BOLD);
foreach ($this->subCommands as $name) {
$output .= " - $name\n";
}
$output .= "\n";
}
return trim($output, "\n ");
}
public function __toString()
{
return $this->render();
}
}

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
class HelpCommand extends Command
{
public function execute(array $args, array $options = array())
{
if (empty($args)) {
$formater = new TextFormater(array('quote' => ' * '));
$this->writeln('Available commands:', Colors::BLACK | Colors::BOLD);
foreach ($this->console->getCommands() as $name => $fqdn) {
if ($fqdn !== __CLASS__) {
$this->writeln($formater->format($name));
}
}
$scriptName = basename($_SERVER['SCRIPT_FILENAME']);
$this->writeln("Use './$scriptName help command' for more info");
} else {
$commandFQDN = $this->console->getCommand($args[0]);
$help = Help::fromFQDN($commandFQDN, Utils::get($args, 1));
$this->writeln($help);
}
}
}

View File

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
interface OptionsParser
{
/**
* Parses the array and returns a tuple containing the arguments and the options
*
* @param array $argv
* @return array
*/
function parse(array $argv);
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* Text writer that writes to stdout or stderr
*/
class StdTextWriter implements TextWriter
{
public function write($text, $pipe = TextWriter::STDOUT)
{
$f = fopen('php://' . $pipe, 'w');
fwrite($f, $text);
fclose($f);
}
public function writeln($text = '', $pipe = TextWriter::STDOUT)
{
$this->write("$text\n", $pipe);
}
}

View File

@ -0,0 +1,194 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
/**
* Utility functions to format text
*/
class TextFormater
{
/** @var int */
public static $defaultIndentWidth = 1;
/** @var int */
protected $indentWidth = 1;
/** @var int */
protected $indent = 0;
/** @var string */
protected $quote = '';
/** @var string */
protected $fgColor;
/** @var string */
protected $bgColor;
/**
* Returns the text formated with the specified options
*
* @param string $text
* @param array $options
* @return string
*/
public static function apply($text, array $options = array())
{
$formater = new TextFormater($options);
return $formater->format($text);
}
/**
* @param array $options
*/
public function __construct(array $options = array())
{
$this->indentWidth = self::$defaultIndentWidth;
$this->setOptions($options);
}
/**
* Available options:
* - indentWidth
* - indent
* - quote
* - fgcolor
* - bgcolor
*
* @param array $options
* @return TextFormater
*/
public function setOptions(array $options)
{
if (isset($options['indentWidth'])) {
$this->setIndentWidth($options['indentWidth']);
}
if (isset($options['indent'])) {
$this->setIndent($options['indent']);
}
if (isset($options['quote'])) {
$this->setQuote($options['quote']);
}
if (isset($options['fgcolor'])) {
$this->setFgColor($options['fgcolor']);
}
if (isset($options['bgcolor'])) {
$this->setBgColor($options['bgcolor']);
}
return $this;
}
/**
* @param int $indent
* @return TextFormater
*/
public function setIndentWidth($width)
{
$this->indentWidth = (int) $width;
return $this;
}
/**
* @return int
*/
public function getIndentWidth()
{
return $this->indentWidth;
}
/**
* @param int $indent
* @return TextFormater
*/
public function setIndent($indent)
{
$this->indent = (int) $indent;
return $this;
}
/**
* @return int
*/
public function getIndent()
{
return $this->indent;
}
/**
* @param string $quote
* @return TextFormater
*/
public function setQuote($quote)
{
$this->quote = $quote;
return $this;
}
/**
* @return string
*/
public function getQuote()
{
return $this->quote;
}
/**
* @param string $color
* @return TextFormater
*/
public function setFgColor($color)
{
$this->fgColor = $color;
return $this;
}
/**
* @return string
*/
public function getFgColor()
{
return $this->fgColor;
}
/**
* @param string $color
* @return TextFormater
*/
public function setBgColor($color)
{
$this->bgColor = $color;
return $this;
}
/**
* @return string
*/
public function getBgColor()
{
return $this->bgColor;
}
/**
* Formats $text according to the formater's options
*
* @param string $text
*/
public function format($text)
{
$lines = explode("\n", $text);
foreach ($lines as &$line) {
$line = ((string) $this->quote)
. str_repeat(' ', $this->indent * $this->indentWidth)
. $line;
}
return Colors::colorize(implode("\n", $lines), $this->fgColor, $this->bgColor);
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
interface TextWriter
{
const STDOUT = 'stdout';
const STDERR = 'stderr';
/**
* Outputs text
*
* @param string $text
*/
function write($test, $pipe = TextWriter::STDOUT);
/**
* Outputs text followed by a line break
*
* @param string $text
*/
function writeln($test = '', $pipe = TextWriter::STDOUT);
}

View File

@ -0,0 +1,189 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit;
use ReflectionFunctionAbstract;
class Utils
{
/**
* Returns the value from $key in $array or $default
*
* @param array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
public static function get($array, $key, $default = null)
{
if (array_key_exists($key, $array)) {
return $array[$key];
}
return $default;
}
/**
* Finds the first file that match the filename in any of
* the specified directories.
*
* @param string $filename
* @param array $dirs
* @return string
*/
public static function find($filename, $dirs = array())
{
if (empty($dirs)) {
if ($filename = realpath($filename)) {
return $filename;
}
} else {
foreach ((array) $dirs as $dir) {
$pathname = self::join($dir, $filename);
if ($pathname = realpath($pathname)) {
return $pathname;
}
}
}
return false;
}
/**
* Extracts files from an array of args
*
* @param array $args
* @param bool $allowWildcards Whether wildcards are allowed
* @return array
*/
public static function filterFiles($args, $allowWildcards = true)
{
$files = array();
foreach ($args as $arg) {
if (file_exists($arg)) {
$files[] = $arg;
} else if ($allowWildcards && strpos($arg, '*') !== false) {
$files = array_merge($files, glob($arg));
}
}
return $files;
}
/**
* Joins paths together
*
* @param string $path1
* @param string $path2
* @param string ...
* @return string
*/
public static function join($path1, $path2) {
$ds = DIRECTORY_SEPARATOR;
return str_replace("$ds$ds", $ds, implode($ds, array_filter(func_get_args())));
}
/**
* Creates a directory recursively
*
* @param string $dir
* @param octal $mode
*/
public static function mkdir($dir, $mode = 0777)
{
if (!file_exists($dir)) {
mkdir($dir, $mode, true);
}
}
/**
* Creates a file and its directory
*
* @param string $filename
* @param string $content
*/
public static function touch($filename, $content = '')
{
self::mkdir(dirname($filename));
file_put_contents($filename, $content);
}
/**
* Returns piped in data
*
* @return string
*/
public static function pipedIn()
{
return file_get_contents('php://stdin');
}
/**
* Returns a dash-cased string into a camelCased string
*
* @param string $string
* @return string
*/
public static function camelize($string)
{
return lcfirst(str_replace(' ', '', ucwords(str_replace('-', ' ', $string))));
}
/**
* Returns a camelCased string into a dash-cased string
*
* @param string $string
* @return string
*/
public static function dashized($string)
{
return strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '-$1', $string));
}
/**
* Creates an array of parameters according to the function definition
*
* @param ReflectionFunctionAbstract $reflection
* @param array $args
* @param array $options
* @param bool $needTagInDocComment Whether the compute-params tag must be present in the doc comment
* @return array
*/
public static function computeFuncParams(ReflectionFunctionAbstract $reflection, array $args, array $options, $needTagInDocComment = true)
{
if ($needTagInDocComment && !preg_match('/@compute-params/', $reflection->getDocComment())) {
return array($args, $options);
}
$nbRequiredParams = $reflection->getNumberOfRequiredParameters();
if (count($args) < $nbRequiredParams) {
throw new ConsoleException("Not enough parameters in '" . $reflection->getName() . "'");
}
$params = $args;
if (count($args) > $nbRequiredParams) {
$params = array_slice($args, 0, $nbRequiredParams);
$args = array_slice($args, $nbRequiredParams);
}
foreach ($reflection->getParameters() as $param) {
if ($param->isOptional() && substr($param->getName(), 0, 1) !== '_') {
if (array_key_exists($param->getName(), $options)) {
$params[] = $options[$param->getName()];
unset($options[$param->getName()]);
} else {
$params[] = $param->getDefaultValue();
}
}
}
$params[] = $args;
$params[] = $options;
return $params;
}
}

View File

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit\Widgets;
use ConsoleKit\TextWriter;
abstract class AbstractWidget
{
/** @var TextWriter */
protected $textWriter;
/**
* @param TextWriter $writer
*/
public function __construct(TextWriter $writer)
{
$this->textWriter = $writer;
}
/**
* @param TextWriter $writer
* @return Dialog
*/
public function setTextWriter(TextWriter $writer)
{
$this->textWriter = $writer;
return $this;
}
/**
* @return TextWriter
*/
public function getTextWriter()
{
return $this->textWriter;
}
}

View File

@ -0,0 +1,131 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit\Widgets;
use ConsoleKit\TextWriter,
ConsoleKit\ConsoleException;
class Box extends AbstractWidget
{
/** @var string */
protected $text;
/** @var string */
protected $lineCharacter;
/** @var int */
protected $padding;
/**
* @param TextWriter $writer
* @param string $text
*/
public function __construct(TextWriter $writer = null, $text = '', $lineCharacter = '*', $padding = 2)
{
$this->textWriter = $writer;
$this->text = $text;
$this->lineCharacter = $lineCharacter;
$this->padding = $padding;
}
/**
* @param string text
* @return Box
*/
public function setText($text)
{
$this->text = $text;
return $this;
}
/**
* @return string
*/
public function getText()
{
return $this->text;
}
/**
* @param string lineCharacter
* @return Box
*/
public function setLineCharacter($lineCharacter)
{
$this->lineCharacter = $lineCharacter;
return $this;
}
/**
* @return string
*/
public function getLineCharacter()
{
return $this->lineCharacter;
}
/**
* @param int padding
* @return Box
*/
public function setPadding($padding)
{
$this->padding = $padding;
return $this;
}
/**
* @return int
*/
public function getPadding()
{
return $this->padding;
}
/**
* @return string
*/
public function render()
{
$lines = explode("\n", $this->text);
$maxWidth = 0;
foreach ($lines as $line) {
if (strlen($line) > $maxWidth) {
$maxWidth = strlen($line);
}
}
$maxWidth += $this->padding * 2 + 2;
$c = $this->lineCharacter;
$output = str_repeat($c, $maxWidth) . "\n";
foreach ($lines as $line) {
$delta = $maxWidth - (strlen($line) + 2 + $this->padding * 2);
$output .= $c . str_repeat(' ', $this->padding) . $line
. str_repeat(' ', $delta + $this->padding) . $c . "\n";
}
$output .= str_repeat($c, $maxWidth);
return $output;
}
public function write()
{
if ($this->textWriter === null) {
throw new ConsoleException('No TextWriter object specified');
}
$this->textWriter->write($this->render());
return $this;
}
public function __toString()
{
return $this->render();
}
}

View File

@ -0,0 +1,104 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit\Widgets;
use ConsoleKit\Colors;
/**
* Creates checklist
*
* <code>
* $checklist = new ConsoleKit\Widgets\Checklist($console);
* $checklist->run(array(
* 'Creating directory structure' => function() {
* // code
* return $success; // bool
* },
* 'Downloading scripts' => function() {
* // code
* return $success; // bool
* }
* ));
* </code>
*
* Will print:
*
* Creating directory structure OK
* Downloading scripts OK
*/
class Checklist extends AbstractWidget
{
protected $maxMessageLength = 100;
protected $successText = 'OK';
protected $successColor = Colors::GREEN;
protected $errorText = 'FAIL';
protected $errorColor = Colors::RED;
public function setMaxMessageLength($length)
{
$this->maxMessageLength = $length;
}
public function getMaxMessageLength()
{
return $this->maxMessageLength;
}
public function setSuccessText($text, $color = Colors::GREEN)
{
$this->successText = $text;
$this->successColor = $color;
}
public function setErrorText($text, $color = Colors::RED)
{
$this->errorText = $text;
$this->errorColor = $color;
}
public function run(array $steps)
{
$maxMessageLength = min(array_reduce(array_keys($steps), function($r, $i) {
return max(strlen($i), $r);
}, 0), $this->maxMessageLength);
foreach ($steps as $message => $callback) {
$this->step($message, $callback, $maxMessageLength);
}
}
public function runArray($array, $message, $callback, $useKeyInMessage = false)
{
$steps = array();
foreach ($array as $k => $v) {
$steps[sprintf($message, $useKeyInMessage ? $k : $v)] = function() use ($k, $v) {
return $callback($v, $k);
};
}
return $this->run($steps);
}
public function step($message, $callback, $maxMessageLength = null)
{
$maxMessageLength = $maxMessageLength ?: $this->maxMessageLength;
$this->textWriter->write(sprintf("%-${maxMessageLength}s", $message));
if (call_user_func($callback)) {
$this->textWriter->write(Colors::colorize($this->successText, $this->successColor));
} else {
$this->textWriter->write(Colors::colorize($this->errorText, $this->errorColor));
}
$this->textWriter->write("\n");
}
}

View File

@ -0,0 +1,68 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit\Widgets;
class Dialog extends AbstractWidget
{
/**
* Writes $text and reads the user's input
*
* @param string $text
* @param string $default
* @param bool $displayDefault
* @return string
*/
public function ask($text, $default = '', $displayDefault = true)
{
if ($displayDefault && !empty($default)) {
$defaultText = $default;
if (strlen($defaultText) > 30) {
$defaultText = substr($default, 0, 30) . '...';
}
$text .= " [$defaultText]";
}
$this->textWriter->write("$text ");
return trim(fgets(STDIN)) ?: $default;
}
/**
* Writes $text (followed by the list of choices) and reads the user response.
* Returns true if it matches $expected, false otherwise
*
* <code>
* if($dialog->confirm('Are you sure?')) { ... }
* if($dialog->confirm('Your choice?', null, array('a', 'b', 'c'))) { ... }
* </code>
*
* @param string $text
* @param string $expected
* @param array $choices
* @param string $default
* @param string $errorMessage
* @return bool
*/
public function confirm($text, $expected = 'y', array $choices = array('Y', 'n'), $default = 'y', $errorMessage = 'Invalid choice')
{
$text = $text . ' [' . implode('/', $choices) . ']';
$choices = array_map('strtolower', $choices);
$expected = strtolower($expected);
$default = strtolower($default);
do {
$input = strtolower($this->ask($text));
if (in_array($input, $choices)) {
return $input === $expected;
} else if (empty($input) && !empty($default)) {
return $default === $expected;
}
$this->textWriter->writeln($errorMessage);
} while (true);
}
}

View File

@ -0,0 +1,190 @@
<?php
/*
* This file is part of the ConsoleKit package.
*
* (c) 2012 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace ConsoleKit\Widgets;
use ConsoleKit\TextWriter,
ConsoleKit\ConsoleException;
/**
* Progress bar
*
* <code>
* $total = 100;
* $progress = new ProgressBar($textWriter, $total);
* for ($i = 0; $i < $total; $i++) {
* $progress->incr();
* usleep(10000);
* }
* $progress->stop();
* </code>
*/
class ProgressBar extends AbstractWidget
{
/** @var int */
protected $value = 0;
/** @var int */
protected $total = 0;
/** @var int */
protected $size = 0;
/** @var bool */
protected $showRemainingTime = true;
/** @var int */
protected $startTime;
/**
* @param TextWriter $writer
* @param int $total
* @param int $size
*/
public function __construct(TextWriter $writer = null, $total = 100, $size = 50, $showRemainingTime = true)
{
$this->textWriter = $writer;
$this->size = $size;
$this->showRemainingTime = $showRemainingTime;
$this->start($total);
}
/**
* @param int $size
* @return ProgressBar
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
/**
* @return int
*/
public function getSize()
{
return $this->size;
}
/**
* @param bool $show
*/
public function setShowRemainingTime($show = true)
{
$this->showRemainingTime = $show;
}
/**
* @return bool
*/
public function getShowRemainingTime()
{
return $this->showRemainingTime;
}
/**
* @param number $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* @return number
*/
public function getValue()
{
return $this->value;
}
/**
* @param int $total
* @return ProgressBar
*/
public function start($total = 100)
{
$this->value = 0;
$this->total = $total;
$this->startTime = time();
return $this;
}
/**
* Increments the value and calls {@see write()}
*
* @param int $increment
* @return ProgressBar
*/
public function incr($increment = 1)
{
$this->value += $increment;
$this->write();
return $this;
}
/**
* Writes a new line
*
* @return ProgressBar
*/
public function stop()
{
$this->textWriter->writeln();
return $this;
}
/**
* Generates the text to write for the current values
*
* @return string
*/
public function render()
{
$percentage = (double) ($this->value / $this->total);
$progress = floor($percentage * $this->size);
$output = "\r[" . str_repeat('=', $progress);
if ($progress < $this->size) {
$output .= ">" . str_repeat(' ', $this->size - $progress);
} else {
$output .= '=';
}
$output .= sprintf('] %s%% %s/%s', round($percentage * 100, 0), $this->value, $this->total);
if ($this->showRemainingTime) {
$speed = (time() - $this->startTime) / $this->value;
$remaining = number_format(round($speed * ($this->total - $this->value), 2), 2);
$output .= " $remaining sec remaining";
}
return $output;
}
/**
* Writes the rendered progress bar to the text writer
*
* @return ProgressBar
*/
public function write()
{
if ($this->textWriter === null) {
throw new ConsoleException('No TextWriter object specified');
}
$this->textWriter->write($this->render());
return $this;
}
public function __toString()
{
return $this->render();
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace ConsoleKit\Tests;
use ConsoleKit\Colors;
class ColorsTest extends ConsoleKitTestCase
{
public function testColorize()
{
$this->assertEquals("\033[31mred\033[0m", Colors::colorize('red', Colors::RED));
$this->assertEquals("\033[1;31mred\033[0m", Colors::colorize('red', Colors::RED | Colors::BOLD));
$this->assertEquals("\033[43m\033[31mred\033[0m", Colors::colorize('red', Colors::RED, Colors::YELLOW));
$this->assertEquals("\033[31mred\033[0m", Colors::red('red'));
}
public function testGetColorCode()
{
$this->assertEquals(Colors::RED, Colors::getColorCode(Colors::RED));
$this->assertEquals(Colors::RED, Colors::getColorCode('red'));
$this->assertEquals(Colors::GREEN, Colors::getColorCode('GREEN'));
}
public function testGetBoldColorCode()
{
$this->assertEquals(Colors::YELLOW | Colors::BOLD, Colors::getColorCode(Colors::YELLOW | Colors::BOLD));
$this->assertEquals(Colors::RED | Colors::BOLD, Colors::getColorCode('red+bold'));
$this->assertEquals(Colors::GREEN | Colors::BOLD, Colors::getColorCode('green', array('bold')));
}
/**
* @expectedException ConsoleKit\ConsoleException
* @expectedExceptionMessage Unknown color 'foobar'
*/
public function testGetUnknownColorCode()
{
Colors::getColorCode('foobar');
}
public function testGetFgColorString()
{
$this->assertEquals("\033[34m", Colors::getFgColorString(Colors::BLUE));
$this->assertEquals("\033[1;34m", Colors::getFgColorString(Colors::BLUE | Colors::BOLD));
}
public function testGetBgColorString()
{
$this->assertEquals("\033[45m", Colors::getBgColorString(Colors::MAGENTA));
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace ConsoleKit\Tests;
abstract class ConsoleKitTestCase extends \PHPUnit_Framework_TestCase
{
}

View File

@ -0,0 +1,127 @@
<?php
namespace ConsoleKit\Tests;
use ConsoleKit\Console,
ConsoleKit\DefaultOptionsParser,
ConsoleKit\EchoTextWriter,
ConsoleKit\Colors;
class ConsoleTest extends ConsoleKitTestCase
{
public function setUp()
{
$this->console = new Console();
$this->console->setTextWriter(new EchoTextWriter());
$this->console->setExitOnException(false);
}
public function testAddCommand()
{
$this->console->addCommand('ConsoleKit\Tests\TestCommand');
$this->assertArrayHasKey('test', $this->console->getCommands());
$this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test'));
$this->console->addCommand('ConsoleKit\Tests\TestCommand', 'test-alias');
$this->assertArrayHasKey('test-alias', $this->console->getCommands());
$this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test-alias'));
$this->console->addCommand('ConsoleKit\Tests\TestStatic::sayHello');
$this->assertArrayHasKey('say-hello', $this->console->getCommands());
$this->assertEquals('ConsoleKit\Tests\TestStatic::sayHello', $this->console->getCommand('say-hello'));
$this->console->addCommand('var_dump');
$this->assertArrayHasKey('var-dump', $this->console->getCommands());
$this->assertEquals('var_dump', $this->console->getCommand('var-dump'));
$this->console->addCommand(array(new TestCommand($this->console), 'execute'), 'test-callback');
$this->assertArrayHasKey('test-callback', $this->console->getCommands());
$this->assertInternalType('array', $this->console->getCommand('test-callback'));
$this->console->addCommand(function($args, $opts) { echo 'hello!'; }, 'hello');
$this->assertArrayHasKey('hello', $this->console->getCommands());
$this->assertInstanceOf('Closure', $this->console->getCommand('hello'));
}
public function testAddCommands()
{
$this->console->addCommands(array(
'ConsoleKit\Tests\TestCommand',
'test-alias' => 'ConsoleKit\Tests\TestCommand'
));
$this->assertArrayHasKey('test', $this->console->getCommands());
$this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test'));
$this->assertArrayHasKey('test-alias', $this->console->getCommands());
$this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test-alias'));
}
public function testAddCommandsFromDir()
{
$this->console->addCommandsFromDir(__DIR__, 'ConsoleKit\Tests');
$this->assertArrayHasKey('test', $this->console->getCommands());
$this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test'));
}
public function testExecute()
{
$this->expectOutputString("hello unknown!\n");
$this->console->addCommand('ConsoleKit\Tests\TestCommand');
$this->console->execute('test');
}
public function testExecuteWithArgs()
{
$this->expectOutputString("hello foo bar!\n");
$this->console->addCommand('ConsoleKit\Tests\TestCommand');
$this->console->execute('test', array('foo', 'bar'));
}
public function testExecuteWithOption()
{
$this->expectOutputString("hello foobar!\n");
$this->console->addCommand('ConsoleKit\Tests\TestCommand');
$this->console->execute('test', array(), array('name' => 'foobar'));
}
public function testExecuteSubCommand()
{
$this->console->addCommand('ConsoleKit\Tests\TestSubCommand', 'test');
$this->assertEquals('hello foobar!', $this->console->execute('test', array('say-hello', 'foobar')));
$this->assertEquals('hi foobar!', $this->console->execute('test', array('say-hi', 'foobar')));
}
public function testExecuteFunction()
{
$this->expectOutputString("\033[31mhello foobar!\033[0m\n");
$this->console->addCommand(function($args, $opts, $console) {
$console->writeln(Colors::colorize(sprintf("hello %s!", $args[0]), $opts['color']));
}, 'test');
$this->console->addCommand('test2', function($args, $opts, $console) {
return "success";
});
$this->console->execute('test', array('foobar'), array('color' => 'red'));
$this->assertEquals("success", $this->console->execute('test2'));
}
public function testRun()
{
$this->expectOutputString("hello unknown!\n");
$this->console->addCommand('ConsoleKit\Tests\TestCommand');
$this->console->run(array('test'));
}
public function testDefaultCommand()
{
$this->expectOutputString("hello unknown!\n");
$this->console->addCommand('ConsoleKit\Tests\TestCommand', null, true);
$this->console->run(array());
}
public function testOneCommandWithArguments() {
$this->expectOutputString("hello foobar!\n");
$this->console->addCommand('ConsoleKit\Tests\TestCommand', null, true);
$this->console->setSingleCommand(true);
$this->console->run(array('foobar'));
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace ConsoleKit\Tests;
use ConsoleKit\DefaultOptionsParser;
class DefaultOptionsParserTest extends ConsoleKitTestCase
{
public function testParse()
{
$parser = new DefaultOptionsParser();
list($args, $options) = $parser->parse(array('arg1'));
$this->assertContains('arg1', $args);
$this->assertEmpty($options);
list($args, $options) = $parser->parse(array('arg1', 'arg2'));
$this->assertContains('arg1', $args);
$this->assertContains('arg2', $args);
$this->assertEmpty($options);
list($args, $options) = $parser->parse(array('--foo', 'arg1'));
$this->assertContains('arg1', $args);
$this->assertArrayHasKey('foo', $options);
list($args, $options) = $parser->parse(array('--foo=bar', '--foobar'));
$this->assertCount(2, $options);
$this->assertArrayHasKey('foo', $options);
$this->assertEquals('bar', $options['foo']);
$this->assertArrayHasKey('foobar', $options);
list($args, $options) = $parser->parse(array('--foo=bar', '--foo=baz'));
$this->assertArrayHasKey('foo', $options);
$this->assertInternalType('array', $options['foo']);
$this->assertContains('bar', $options['foo']);
$this->assertContains('baz', $options['foo']);
list($args, $options) = $parser->parse(array('-a', '-bc'));
$this->assertCount(3, $options);
$this->assertArrayHasKey('a', $options);
$this->assertArrayHasKey('b', $options);
$this->assertArrayHasKey('c', $options);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace ConsoleKit\Tests;
class TestCommand extends \ConsoleKit\Command
{
public function execute(array $args, array $opts = array())
{
$name = 'unknown';
if (!empty($args)) {
$name = implode(' ', $args);
} else if (isset($opts['name'])) {
$name = $opts['name'];
}
$this->writeln(sprintf("hello %s!", $name));
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace ConsoleKit\Tests;
class TestStatic
{
public function sayHello(array $args, array $opts = array(), $console)
{
$name = 'unknown';
if (!empty($args)) {
$name = implode(' ', $args);
} else if (isset($opts['name'])) {
$name = $opts['name'];
}
$console->writeln(sprintf("hello %s!", $name));
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace ConsoleKit\Tests;
class TestSubCommand extends \ConsoleKit\Command
{
public function executeSayHello(array $args, array $opts)
{
return sprintf("hello %s!", $args[0]);
}
/**
* @compute-params
*/
public function executeSayHi($name)
{
return sprintf("hi %s!", $name);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace ConsoleKit\Tests;
use ConsoleKit\TextFormater;
class TextFormaterTest extends ConsoleKitTestCase
{
public function setUp()
{
$this->formater = new TextFormater();
$this->formater->setOptions(array(
'indentWidth' => 4,
'indent' => 1,
'quote' => '>',
'fgcolor' => 'black',
'bgcolor' => 'white'
));
}
public function testSetOptions()
{
$this->assertEquals(4, $this->formater->getIndentWidth());
$this->assertEquals(1, $this->formater->getIndent());
$this->assertEquals('>', $this->formater->getQuote());
$this->assertEquals('black', $this->formater->getFgColor());
$this->assertEquals('white', $this->formater->getBgColor());
}
public function testFormat()
{
$expected = "\033[47m\033[30m> my text\033[0m";
$this->assertEquals($expected, $this->formater->format('my text'));
}
public function testFormatMultiline()
{
$expected = "\033[47m\033[30m> line1\n> line2\033[0m";
$this->assertEquals($expected, $this->formater->format("line1\nline2"));
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace ConsoleKit\Tests;
use ConsoleKit\Utils;
class UtilsTests extends ConsoleKitTestCase
{
public function testGet()
{
$data = array('foo' => 'bar');
$this->assertEquals('bar', Utils::get($data, 'foo'));
$this->assertNull(Utils::get($data, 'unknown'));
$this->assertEquals('default', Utils::get($data, 'unknown', 'default'));
}
public function testFind()
{
$this->assertEquals(realpath(__FILE__), Utils::find(basename(__FILE__), __DIR__));
}
public function testFilterFiles()
{
$this->assertEquals(array(__FILE__), Utils::filterFiles(array(__FILE__, 'not_existant_file')));
}
public function testJoin()
{
$path = "foo" . DIRECTORY_SEPARATOR . "bar";
$this->assertEquals($path, Utils::join('foo', 'bar'));
}
public function testCamelize()
{
$this->assertEquals('fooBar', Utils::camelize('foo-bar'));
}
public function testDashized()
{
$this->assertEquals('foo-bar', Utils::dashized('fooBar'));
}
}

View File

@ -0,0 +1,14 @@
<?php
set_include_path(implode(PATH_SEPARATOR, array(
__DIR__,
__DIR__ . '/../src',
get_include_path()
)));
spl_autoload_register(function($className) {
if (substr($className, 0, 10) === 'ConsoleKit') {
$filename = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\')) . '.php';
require_once $filename;
}
});