Browse Source

Grands changements dans Spip-Cli (en cours)

Maintenant que merveilleusement le répertoire vendor est sorti et qu’on a une installation normale par Composer,
on se rapproche un peu plus d’une arborescence de paquet Composer classique.

TL;DR (comme dirait James)
-----
```
svn up
composer update
cd bin
rm /usr/local/bin/spip
ln -s $(pwd)/spip /usr/local/bin/spip
# linux
rm /etc/bash_completion.d/spip
ln -s $(pwd)/spip_console_autocomplete /etc/bash_completion.d/spip
# macos
rm /usr/local/etc/bash_completion.d/spip
ln -s $(pwd)/spip_console_autocomplete /usr/local/etc/bash_completion.d/spip
```

Les exécutables :
-----------------
- On se crée un répertoire "bin" pour y mettre les exécutables (spip et spip_console_autocomplete)
- On déclare ces binaires à Composer, de sorte que si un autre paquet composer nécessite ce paquet, 
  les binaires seront liés dans vendor/bin/ plutôt que perdus quelque part dans vendor/...
- Par gentillesse, on conserve les anciens exécutables (spip.php et spip_completion.sh) à la racine (dépréciés)
  qui redirigent simplement sur les nouveaux. Il convient de modifier les liens symboliques
  qui avait été fait sur le serveur (ie: /usr/local/bin/spip et /etc/bash_completion.d/spip ou /usr/local/etc/bash_completion.d/spip)
- L’executable "spip" ne fait maintenant qu’executer l’application idoine (src/Application) et n’a pas de code en elle même.

 Les sources Spip-cli
---------------------
- Un nouveau répertoire 'src' regroupe de nouveaux fichiers gérant Spip-Cli ainsi que ses commandes fournies de base.
- Ce répertoire est dans le namespace 'Spip\Cli' déclaré à composer (et donc en autoloding).
- Les commandes Spip Cli se retrouvent dans src/Command, actuellement sans modification hormis l’ajout du namespace.
- src/Application étend l’application Console de sympfony. C’est ce fichier essentiellement (avec src/Loader/Spip) qui reçoit 
  l’ancien code de l’executable spip.php. Il se charge de : 
* Déclarer certains services (qui seront accessibles aux commandes via `$this->getApplication()->getService('xxx')` par exemple
* Déclarer les commandes natives de Spip-cli
* Démarrer SPIP (s’il est installé) [note] Je ne suis pas en accord avec cela et préfèrerais que chaque commande décide ou non d’elle même de démarrer SPIP ou pas, mais bon
* Déclarer les commandes contenus dans les plugins SPIP, dans les répertoires 'spip-cli' (si SPIP a démarré)

On tente un poil de s’approcher du fonctionnement de Cilex (comme j’avais fait avec l’essai Polatouche). J’ai hésité à le reprendre directement ici,
mais celui ci actuellement nécessite PHP 5.5.9 alors qu’actuellement Spip-cli est encore plus tolérant (5.4).
Du coup, je n’ai repris que l’utilisation de Pimple en tant que conteneur de configuration / services.

Des styles d’écriture
---------------------

J’ai ajouté un autre morceau de Polatouche qui était l’utilisation de SymfonyStyle pour écrire plus facilement les textes affichés sur la console.
Je l’avais étendu avec d’autres méthodes, et j’ai remis ce morceau ici également. Dans src/Console/Style/SpipCliStyle.

Ainsi, une commande peut faire :
```
$io = new Spip\Cli\Console\Style\SpipCliStyle($input, $output);
$io->title("Le joli titre");
```

C’est aussi (et de préférence) accessible avec le conteneur de service, avec le service 'console.io',
mais une méthode getIo() le récupère également :
```
/** @var SpipCliStyle $io */
$io = $this->getApplication()->getIo($input, $output);
$io->title($this->getDescription());
```

En plus les styles/méthodes que propose la classe symfony (warning, error, note, title, section, table, ...),
SpipCliStyle propose d’autres facilités comme "check, fail, care" qui ajoutent avant le texte à afficher
une icone de réussite, d’échec ou d’attention. Il y a également "atable" pour afficher rapidement un tableau cle/valeur, 
ou "columns" pour écrire en colonne…

Loader SQL
----------

J’en parle rapidement, mais je n’ai pas fini de le retester, je remets aussi une classe qui est capable 
d’analyser un fichier de connexion SQL SPIP et de génerer une instance de PDO avec, afin de réaliser des 
requetes SQL en dehors de l’API de SPIP (et de son chargement éventuel).


Objectifs à venir et notes
--------------------------

L’objectif à travers ces modifications est multiple
- pouvoir réutiliser plus facilement des choses que j’avais mises dans Polatouche…
- pouvoir utiliser spip-cli sur un spip mutualisé (l’accès sql va être important pour obtenir l’url du site avant de charger spip) et
  exécuter une commande sur un ensemble de sites/ d’une mutualisation.
- pouvoir lancer les mises à jour de BDD du core / des plugins en créant une commande pour le faire.
svn/root/trunk
marcimat@rezo.net 3 years ago
parent
commit
7ab8254c19
  1. 43
      .gitattributes
  2. 2
      .gitignore
  3. 17
      bin/spip
  4. 76
      bin/spip_console_autocomplete
  5. 15
      composer.json
  6. 186
      composer.lock
  7. 214
      spip.php
  8. 77
      spip_completion.sh
  9. 210
      src/Application.php
  10. 2
      src/Command/CacheDesactiver.php
  11. 2
      src/Command/CacheReactiver.php
  12. 2
      src/Command/CacheVider.php
  13. 2
      src/Command/CoreInstaller.php
  14. 2
      src/Command/CoreListerVersions.php
  15. 2
      src/Command/CoreMettreajour.php
  16. 2
      src/Command/CorePreparer.php
  17. 2
      src/Command/CoreTelecharger.php
  18. 2
      src/Command/IntegraalGenerer.php
  19. 2
      src/Command/PhpEval.php
  20. 2
      src/Command/PluginsActiver.php
  21. 2
      src/Command/PluginsDesactiver.php
  22. 2
      src/Command/PluginsLister.php
  23. 2
      src/Command/PluginsSvpDepoter.php
  24. 2
      src/Command/PluginsSvpTelecharger.php
  25. 2
      src/Command/ServerLocate.php
  26. 2
      src/Command/TextePropre.php
  27. 2
      src/Command/TexteTypo.php
  28. 123
      src/Console/Style/SpipCliStyle.php
  29. 234
      src/Loader/Spip.php
  30. 156
      src/Loader/Sql.php
  31. 75
      src/Tools/Files.php

43
.gitattributes

@ -1,24 +1,31 @@
* text=auto !eol
/README.md -text
bin/spip -text
bin/spip_console_autocomplete -text
/composer.json -text
/composer.lock -text
spip-cli/CacheDesactiver.php -text
spip-cli/CacheReactiver.php -text
spip-cli/CacheVider.php -text
spip-cli/CoreInstaller.php -text
spip-cli/CoreListerVersions.php -text
spip-cli/CoreMettreajour.php -text
spip-cli/CorePreparer.php -text
spip-cli/CoreTelecharger.php -text
spip-cli/IntegraalGenerer.php -text
spip-cli/PhpEval.php -text
spip-cli/PluginsActiver.php -text
spip-cli/PluginsDesactiver.php -text
spip-cli/PluginsLister.php -text
spip-cli/PluginsSvpDepoter.php -text
spip-cli/PluginsSvpTelecharger.php -text
spip-cli/ServerLocate.php -text
spip-cli/TextePropre.php -text
spip-cli/TexteTypo.php -text
/spip.php -text
/spip_completion.sh -text
src/Application.php -text
src/Command/CacheDesactiver.php -text
src/Command/CacheReactiver.php -text
src/Command/CacheVider.php -text
src/Command/CoreInstaller.php -text
src/Command/CoreListerVersions.php -text
src/Command/CoreMettreajour.php -text
src/Command/CorePreparer.php -text
src/Command/CoreTelecharger.php -text
src/Command/IntegraalGenerer.php -text
src/Command/PhpEval.php -text
src/Command/PluginsActiver.php -text
src/Command/PluginsDesactiver.php -text
src/Command/PluginsLister.php -text
src/Command/PluginsSvpDepoter.php -text
src/Command/PluginsSvpTelecharger.php -text
src/Command/ServerLocate.php -text
src/Command/TextePropre.php -text
src/Command/TexteTypo.php -text
src/Console/Style/SpipCliStyle.php -text
src/Loader/Spip.php -text
src/Loader/Sql.php -text
src/Tools/Files.php -text

2
.gitignore

@ -1,2 +1,2 @@
spip-cli/.git*
src/Command/.git*
/vendor

17
bin/spip

@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/../../../autoload.php')) {
require_once __DIR__ . '/../../../autoload.php';
} else {
throw new \Exception("Can't find autoloader. Need Composer install ?");
}
use Spip\Cli\Application;
#ini_set('display_errors', 'On');
$app = new Application();
$app->run();

76
bin/spip_console_autocomplete

@ -0,0 +1,76 @@
#!/bin/sh
#
# Symfony2 App/Console autocompletion (commands and arguments only)
# Copyright (c) 2014, Joshua Thijssen
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# * Neither the name of the {organization} nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# Usable for both bash and zsh (probably)
#
# Usage:
# Load the script (or add to your .bashrc)
#
# source ./console_completion.sh
#
# Autocomplete:
#
# ./app/console <TAB>
#
# Will autocomplete both commands and its arguments.
#
if [[ -n ${ZSH_VERSION-} ]]; then
autoload -U +X bashcompinit && bashcompinit
fi
_complete_spip_app_console() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
# Assume first word is the actual app/console command
console="${COMP_WORDS[0]}"
if [[ ${COMP_CWORD} == 1 ]] ; then
# No command found, return the list of available commands
#cmds=` ${console} --no-ansi | sed -n -e '/^Available commands/,//p' | grep -n '^ ' | sed -e 's/^ \+//' | awk '{ print $2 }'`
# seulement si la commande a une description, ce qui permet de ne pas présenter le nom d’une commande racine (ie: 'spip' si commande 'spip:sql:dump')
cmds=` ${console} --no-ansi | sed -n -e '/^Available commands/,//p' | grep -n '^ ' | sed -e 's/^ \+//' | awk '{ if ($3) { print $2} }'`
else
# Commands found, parse options
cmds=` ${console} ${COMP_WORDS[1]} --no-ansi --help | sed -n -e '/^Options/,/^$/p' | grep -n '^ ' | sed -e 's/^ \+//' | awk '{ print $2 }'`
fi
COMPREPLY=( $(compgen -W "${cmds}" -- ${cur}) )
return 0
}
export COMP_WORDBREAKS="\ \"\\'><=;|&("
complete -F _complete_spip_app_console spip

15
composer.json

@ -1,13 +1,24 @@
{
"name": "spip/spip-cli",
"description" : "Command Line for SPIP",
"keywords" : ["spip","cli"],
"minimum-stability": "stable",
"require": {
"php": "^5.4|^7",
"symfony/console": "^2.8"
"symfony/console": "^2.8",
"symfony/finder": "^2.8",
"pimple/pimple" : "^3.2"
},
"config": {
"platform": {
"php": "5.4.45"
}
}
},
"autoload": {
"psr-4": {"Spip\\Cli\\": "src"}
},
"bin": [
"bin/spip",
"bin/spip_console_autocomplete"
]
}

186
composer.lock

@ -1,11 +1,110 @@
{
"_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",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a91ac1874d9da17b1d4a34c6c15e6f53",
"content-hash": "fd4b6a8b34cc1d268719913a320e7576",
"packages": [
{
"name": "pimple/pimple",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/log",
"version": "1.0.2",
@ -55,16 +154,16 @@
},
{
"name": "symfony/console",
"version": "v2.8.36",
"version": "v2.8.41",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "a6ff8b2ffa4eb43046828b303af2e3fedadacc27"
"reference": "e8e59b74ad1274714dad2748349b55e3e6e630c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/a6ff8b2ffa4eb43046828b303af2e3fedadacc27",
"reference": "a6ff8b2ffa4eb43046828b303af2e3fedadacc27",
"url": "https://api.github.com/repos/symfony/console/zipball/e8e59b74ad1274714dad2748349b55e3e6e630c7",
"reference": "e8e59b74ad1274714dad2748349b55e3e6e630c7",
"shasum": ""
},
"require": {
@ -78,7 +177,7 @@
"symfony/process": "~2.1|~3.0.0"
},
"suggest": {
"psr/log": "For using the console logger",
"psr/log-implementation": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/process": ""
},
@ -112,20 +211,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2018-02-26T15:33:21+00:00"
"time": "2018-05-15T21:17:45+00:00"
},
{
"name": "symfony/debug",
"version": "v2.8.36",
"version": "v2.8.41",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "f693ba88189b6384370c13d114cfd010649c31af"
"reference": "fe8838e11cf7dbaf324bd6f51d065d873ccf78a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/f693ba88189b6384370c13d114cfd010649c31af",
"reference": "f693ba88189b6384370c13d114cfd010649c31af",
"url": "https://api.github.com/repos/symfony/debug/zipball/fe8838e11cf7dbaf324bd6f51d065d873ccf78a2",
"reference": "fe8838e11cf7dbaf324bd6f51d065d873ccf78a2",
"shasum": ""
},
"require": {
@ -169,20 +268,69 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2018-02-28T21:47:46+00:00"
"time": "2018-05-15T21:17:45+00:00"
},
{
"name": "symfony/finder",
"version": "v2.8.41",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "79764d21163db295f0daf8bd9d9b91f97e65db6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/79764d21163db295f0daf8bd9d9b91f97e65db6a",
"reference": "79764d21163db295f0daf8bd9d9b91f97e65db6a",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2018-05-15T21:17:45+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.7.0",
"version": "v1.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
"reference": "3296adf6a6454a050679cde90f95350ad604b171"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
"reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171",
"reference": "3296adf6a6454a050679cde90f95350ad604b171",
"shasum": ""
},
"require": {
@ -194,7 +342,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
"dev-master": "1.8-dev"
}
},
"autoload": {
@ -228,7 +376,7 @@
"portable",
"shim"
],
"time": "2018-01-30T19:27:44+00:00"
"time": "2018-04-26T10:06:28+00:00"
}
],
"packages-dev": [],

214
spip.php

@ -1,215 +1,5 @@
#!/usr/bin/env php
<?php
// spip
$dossier_cli = dirname(__FILE__);
include_once "$dossier_cli/vendor/autoload.php";
use Symfony\Component\Console\Application;
if( ! ini_get('date.timezone') ) { date_default_timezone_set('GMT'); }
/**
* Trouver toutes les sous-classes qui étendent une classe donnée
*
* @param string $parent
* Nom de la classe dont on veut chercher les extensions.
* @return array
* Retourne un tableau de chaînes contenant chacune le nom d'une classe.
*/
function getSubclassesOf($parent) {
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, $parent)) {
$result[] = $class;
}
}
return $result;
}
/**
* Transforme un chemin avec les bons séparateurs de dossiers
*
* Si on est sur un OS qui n'utilise pas des / comme séparateur de
* dossier dans les chemins, on remplace les / par le bon
* séparateur.
*
* @param string $path
* Un chemin au format UNIX.
* @return string
* Retourne le même chemin au format approprié à
* l'environnement dans lequel on se trouve.
*/
function prep_path($path) {
if (DIRECTORY_SEPARATOR !== '/') {
return str_replace('/', DIRECTORY_SEPARATOR, $path);
} else {
return $path;
}
}
/**
* Cherche la racine d'un site SPIP
*
* Retourne le chemin absolu vers la racine du site SPIP dans
* lequel se trouve le répertoire courant. Retourne FALSE si l'on
* est pas dans l'arborescence d'un site SPIP.
*
* @return string|bool
* Retourne le chemin vers la racine du SPIP dans lequel on se trouve.
* Retourne false si on n'est pas dans l'arborescence d'une installation SPIP.
*/
function spip_chercher_racine() {
$cwd = getcwd();
while ($cwd) {
if (file_exists(prep_path("$cwd/ecrire/inc_version.php"))) {
return $cwd;
} else {
/* On remonte d'un dossier dans l'arborescence */
$cwd_array = explode(DIRECTORY_SEPARATOR, $cwd);
array_pop($cwd_array);
$cwd = implode(DIRECTORY_SEPARATOR, $cwd_array);
}
}
return false;
}
/**
* Lance le SPIP dans lequel on se trouve
*
* Inclut ecrire/inc_version.php, ce qui permet ensuite d'utiliser
* toutes les fonctions de SPIP comme lors du chargement d'une
* page.
*
* @param string $spip_racine
* Le chemin vers la racine du SPIP que l'on veut charger.
* @return bool
* Retourne true si on a pu charger SPIP correctement, false sinon.
*/
function spip_charger($spip_racine) {
// On liste toutes les globales déclarées au démarrage de SPIP (55 !!)
global
$nombre_de_logs,
$taille_des_logs,
$table_prefix,
$cookie_prefix,
$dossier_squelettes,
$filtrer_javascript,
$type_urls,
$debut_date_publication,
$ip,
$mysql_rappel_connexion,
$mysql_rappel_nom_base,
$test_i18n,
$ignore_auth_http,
$ignore_remote_user,
$derniere_modif_invalide,
$quota_cache,
$home_server,
$help_server,
$url_glossaire_externe,
$tex_server,
$traiter_math,
$xhtml,
$xml_indent,
$source_vignettes,
$formats_logos,
$controler_dates_rss,
$spip_pipeline,
$spip_matrice,
$plugins,
$surcharges,
$exceptions_des_tables,
$tables_principales,
$table_des_tables,
$tables_auxiliaires,
$table_primary,
$table_date,
$table_titre,
$tables_jointures,
$liste_des_statuts,
$liste_des_etats,
$liste_des_authentifications,
$spip_version_branche,
$spip_version_code,
$spip_version_base,
$spip_sql_version,
$spip_version_affichee,
$visiteur_session,
$auteur_session,
$connect_statut,
$connect_toutes_rubriques,
$hash_recherche,
$hash_recherche_strict,
$ldap_present,
$meta,
$connect_id_rubrique,
$puce;
// Pour que les include dans les fichiers php de SPIP fonctionnent correctement,
// il faut être à la racine du site.
// On change de répertoire courant le temps de charger tout ça.
$cwd = getcwd();
chdir($spip_racine);
// Si jamais la base n'est pas installé on anhile la redirection et on affirme qu'on est sur la page d'installation
if (!is_file('config/connect.php')) {
$_GET['exec'] = 'install';
define('_FILE_CONNECT', 'config/connect.tmp.php');
}
// TIMEOUT de 24h…
define('_UPGRADE_TIME_OUT', 24*3600);
// On charge la machinerie de SPIP
include_once prep_path("$spip_racine/ecrire/inc_version.php");
// On revient dans le répertoire dans lequel la commande a été appelée
chdir($cwd);
// Si _ECRIRE_INC_VERSION existe, inc_version.php a été chargé correctement
if (_ECRIRE_INC_VERSION) {
// Charge l'API SQL, pour être sûr de l'avoir déjà
include_spip('base/abstract_sql');
// Tout s'est bien passé
return TRUE;
} else {
// Mauvais chargement
return FALSE;
}
}
// Création de la ligne de commande
$spip = new Application('Ligne de commande pour SPIP', '0.2.3');
// Inclusion des fichiers contenant les commandes de base
foreach (glob("$dossier_cli/spip-cli/*.php") as $commande_fichier) {
include_once $commande_fichier;
}
if (($spip_racine = spip_chercher_racine()) and spip_charger($spip_racine)) {
$spip_loaded = TRUE;
// charger toutes les commandes qui se trouvent dans le path de SPIP.
$cwd = getcwd();
chdir($spip_racine);
$commandes = find_all_in_path('spip-cli/', '.*[.]php$');
foreach ($commandes as $commande_fichier) {
include_once $commande_fichier;
}
chdir($cwd);
} else {
$spip_loaded = FALSE;
}
// Ajouter automatiquement toutes les commandes trouvées (= un objet de chaque classe Command)
if ($commandes = getSubclassesOf('Symfony\Component\Console\Command\Command')){
foreach ($commandes as $class){
$spip->add(new $class);
}
}
// Lancement de l'application
$spip->run();
echo "\n[deprecated] Veuillez utiliser les executables SPIP-Cli du répertoire bin.\n";
include_once(__DIR__ . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'spip');

77
spip_completion.sh

@ -1,77 +1,4 @@
#!/bin/sh
#
# Source :
# https://github.com/jaytaph/SFConsole/blob/master/console_completion.sh
#
# Symfony2 App/Console autocompletion (commands and arguments only)
# Copyright (c) 2014, Joshua Thijssen
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# * Neither the name of the {organization} nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# Usable for both bash and zsh (probably)
#
# Usage:
# Load the script (or add to your .bashrc)
#
# source ./console_completion.sh
#
# Autocomplete:
#
# ./app/console <TAB>
#
# Will autocomplete both commands and its arguments.
#
if [[ -n ${ZSH_VERSION-} ]]; then
autoload -U +X bashcompinit && bashcompinit
fi
_complete_sf2_app_console() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
# Assume first word is the actual app/console command
console="${COMP_WORDS[0]}"
if [[ ${COMP_CWORD} == 1 ]] ; then
# No command found, return the list of available commands
cmds=` ${console} --no-ansi | sed -n -e '/^Available commands/,//p' | grep -n '^ ' | sed -e 's/^ \+//' | awk '{ print $2 }'`
else
# Commands found, parse options
cmds=` ${console} ${COMP_WORDS[1]} --no-ansi --help | sed -n -e '/^Options/,/^$/p' | grep -n '^ ' | sed -e 's/^ \+//' | awk '{ print $2 }'`
fi
COMPREPLY=( $(compgen -W "${cmds}" -- ${cur}) )
return 0
}
export COMP_WORDBREAKS="\ \"\\'><=;|&("
complete -F _complete_sf2_app_console spip
# Deprecated : utiliser/lier bin/spip_console_autocomplete
$(dirname $0)/bin/spip_console_autocomplete

210
src/Application.php

@ -0,0 +1,210 @@
<?php
namespace Spip\Cli;
use Pimple\Container;
use Spip\Cli\Console\Style\SpipCliStyle;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
class Application extends ConsoleApplication {
const NAME = "Spip Cli";
const VERSION = "0.4.0";
protected $options = [];
protected $container;
/**
* Application constructor.
*
* Charge automatiquement les commandes présentes
* dans le répertoire Command de ce projet.
*
* @param array $options
*/
public function __construct(array $options = []) {
parent::__construct(self::NAME, self::VERSION);
$this->container = new Container([
'debug' => false,
'spip.directory' => null,
'spip.webmestre.email' => '',
'spip.webmestre.login' => '',
'spip.webmestre.nom' => '',
'spip.webmestre.login.prefixe' => 'SpipCli-',
]);
// todo: config file
foreach ($options as $key => $value) {
$this->container[$key] = $value;
}
$this->setTimezone();
$this->registerServices();
$this->registerCommandsInProject(self::class);
$this->loadSpip(); // hum…
$this->registerCommandsInSpip();
}
protected function registerServices() {
$app = $this->container;
$app['console.io'] = function() {
return function(InputInterface $input = null, OutputInterface $output = null) {
if (null === $input) {
$input = new ArgvInput();
}
if (null === $output) {
$output = new ConsoleOutput();
}
return new Console\Style\SpipCliStyle($input, $output);
};
};
$app['spip.loader'] = function ($app) {
$spip = new Loader\Spip($app['spip.directory']);
$spip->setContainer($app);
return $spip;
};
$app['spip.sql'] = function ($app) {
$connect = $app['spip.loader']->getPathconnect();
if (!is_file($connect)) {
throw new \Exception('SPIP database is not configured');
}
return new Loader\Sql($connect);
};
}
/**
* @note
* Alors ça, il faudrait plutôt que ça soit les commandes
* qui demandent le chargement de SPIP si elles en ont besoin,
* afin de faire des commandes SPIP n’est pas chargé encore…
*/
public function loadSpip() {
try {
$spip = $this->container['spip.loader'];
$spip->load();
} catch (\Exception $e) {
$this->getIo()->note($e->getMessage());
}
}
/**
* Ajoute les commandes contenues dans le répertoire Command du même chemin que la classe transmise
*/
public function registerCommandsInProject($class) {
foreach ($this->findCommandsInProject($class) as $command) {
if (class_exists($command)) {
$this->add(new $command());
}
}
}
/**
* Retourne la liste des commandes au même niveau que ce projet
* @return array
* @throws \ReflectionException
*/
public function findCommandsInProject($class) {
$class = new \ReflectionClass($class);
$commandDir = dirname($class->getFilename()) . '/Command'; // .../spip-cli/src/Command
$list = [];
if (is_dir($commandDir)) {
$namespace = $class->getNamespaceName(); // Spip\Cli
$finder = new Finder();
$finder->files()->in($commandDir)->name('*.php');
foreach ($finder as $file) {
// Spip\Cli\Command\Name
$list[] = $namespace . '\\Command\\' . str_replace('/', '\\', substr($file->getRelativePathname(), 0, -4));
}
}
return $list;
}
/**
* Chargement automatique des commandes spip-cli/ présentes dans les répertoires spip-cli/
* des plugins SPIP
*/
public function registerCommandsInSpip() {
// les commandes dans spip-cli/ des plugins SPIP actifs
$this->registerSpipCliPluginsCommands();
// charger les vilaines globales utilisées dans les commandes spip-cli
try {
global $spip_racine, $spip_loaded, $cwd;
$cwd = getcwd();
$spip = $this->container['spip.loader'];
$spip_racine = $spip->getDirectory();
$spip_loaded = $spip->load();
} catch (\Exception $e) {
return false;
}
}
/**
* Chargement des commandes présentes dans 'spip-cli/' des plugins SPIP actifs.
*
* Nécessite de démarrer SPIP (et donc d’être dans un SPIP)
* @return bool
*/
public function registerSpipCliPluginsCommands() {
try {
$spip = $this->container['spip.loader'];
if (!$spip->load()) {
return false;
}
} catch (\Exception $e) {
return false;
}
$commandes = find_all_in_path('spip-cli/', '.*[.]php$');
foreach ($commandes as $path) {
$this->registerSpipCliCommand($path);
}
}
/**
* Déclare une commande "Spip Cli"
* @return bool
*/
public function registerSpipCliCommand($path) {
include_once($path);
$command = '\\' . basename($path, '.php');
if ($namespace = Tools\Files::getNamespace($path)) {
$command = '\\' . $namespace . $command;
}
if (class_exists($command)) {
$this->add(new $command());
}
}
/**
* If the timezone is not set anywhere, set it to UTC.
* @return void
*/
protected function setTimezone() {
if (false == ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
}
public function getContainer() {
return $this->container;
}
public function getService($name) {
return $this->container[$name];
}
/**
* @param InputInterface|null $input
* @param OutputInterface|null $output
* @return SpipCliStyle
*/
public function getIo(InputInterface $input = null, OutputInterface $output = null) {
return $this->container['console.io']($input, $output);
}
}

2
spip-cli/CacheDesactiver.php → src/Command/CacheDesactiver.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CacheReactiver.php → src/Command/CacheReactiver.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CacheVider.php → src/Command/CacheVider.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CoreInstaller.php → src/Command/CoreInstaller.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CoreListerVersions.php → src/Command/CoreListerVersions.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CoreMettreajour.php → src/Command/CoreMettreajour.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CorePreparer.php → src/Command/CorePreparer.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/CoreTelecharger.php → src/Command/CoreTelecharger.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/IntegraalGenerer.php → src/Command/IntegraalGenerer.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/PhpEval.php → src/Command/PhpEval.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/PluginsActiver.php → src/Command/PluginsActiver.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/PluginsDesactiver.php → src/Command/PluginsDesactiver.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/PluginsLister.php → src/Command/PluginsLister.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/PluginsSvpDepoter.php → src/Command/PluginsSvpDepoter.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/PluginsSvpTelecharger.php → src/Command/PluginsSvpTelecharger.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/ServerLocate.php → src/Command/ServerLocate.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/TextePropre.php → src/Command/TextePropre.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

2
spip-cli/TexteTypo.php → src/Command/TexteTypo.php

@ -1,5 +1,7 @@
<?php
namespace Spip\Cli\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

123
src/Console/Style/SpipCliStyle.php

@ -0,0 +1,123 @@
<?php
namespace Spip\Cli\Console\Style;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Ajout de helpers en suppléments des Styles Symfony
*/
class SpipCliStyle extends SymfonyStyle {
public function check($message) {
$this->prependText($message, ' <fg=green>✔</> ');
}
public function fail($message) {
return $this->prependText($message, ' <fg=red>✘</> ');
}
public function care($message) {
return $this->prependText($message, ' <fg=yellow;options=bold>!</> ');
}
public function prependText($message, $prepend) {
$messages = is_array($message) ? array_values($message) : array($message);
foreach ($messages as $message) {
$this->text($prepend . $message);
}
}
/** Simplifier ->table() avec les tableaux ayant des clés */
public function atable($rows) {
if (count($rows)) {
$keys = array_keys(reset($rows));
// reordonner toujours dans le même ordre de clés chaque ligne
foreach ($rows as $k => $row) {
$rows[$k] = array_map(function($key) use ($row) {
return $row[$key];
}, $keys);
}
$this->table($keys, $rows);
}
}
/**
* Affiche une liste d’éléments sur n colonnes
*
* Utile pour présenter une longue liste sur l’écran
*
* $io->columns($liste, 6, true);
*
* @see Command::columns()
* @param array $list Le tableau unidimensionel
* @param int $columns Le nombre de colonnes souhaitées
* @param bool $flip Change l’ordre des éléments
* Si A, B, C sont les premières entrées du tableau $array
* - false : A, B, C sera en première ligne,
* - true : A, B, C sera en première colonne
* @return array[]
*/
public function columns($list, $columns = 4, $flip = false) {
if (count($list) < $columns) {
$columns = max(1, count($list));
}
$tab = self::columnize($list, $columns, $flip);
$this->table([], $tab);
}
/**
* Transforme un tableau en tableau de n colonnes
*
* Utile pour présenter une longue liste sur l’écran
*
* $liste = SpipCliStyle::columnize($liste, 6, true);
* $io->table([], $liste);
*
* @param array $list Le tableau unidimensionel
* @param int $columns Le nombre de colonnes souhaitées
* @param bool $flip Change l’ordre des éléments
* Si A, B, C sont les premières entrées du tableau $array
* - false : A, B, C sera en première ligne,
* - true : A, B, C sera en première colonne
* @return array[]
*/
static public function columnize(array $list, $columns = 6, $flip = false) {
$nb = count($list);
if ($mod = $nb % $columns) {
$list = array_pad($list, $nb + $columns - $mod, null);
}
$list = array_chunk($list, $columns);
if ($flip) {
$list = self::flip($list);
}
return $list;
}
/**
* Flip bidimensional array
* @link https://stackoverflow.com/questions/2221476/php-how-to-flip-the-rows-and-columns-of-a-2d-array
*/
static public function flip($arr) {
$rows = count($arr);
$ridx = 0;
$cidx = 0;
$out = array();
foreach($arr as $rowidx => $row){
foreach($row as $colidx => $val){
$out[$ridx][$cidx] = $val;
$ridx++;
if($ridx >= $rows){
$cidx++;
$ridx = 0;
}
}
}
return $out;
}
}

234
src/Loader/Spip.php

@ -0,0 +1,234 @@
<?php
namespace Spip\Cli\Loader;
use Pimple\Container;
use Spip\Cli\Tools\Files;
/**
* Chargement de SPIP
* @api
*/
class Spip {
/** @var string Chemin du démarreur */
protected $starter = 'ecrire/inc_version.php';
/** @var string Chemin du connecteur SQL */
protected $connect = 'config/connect.php';
/** @var string */
private $directory;
/** @var bool */
private $loaded = false;
/** @var bool */
private $exists;
/** @var Container */
private $app;
/**
* Loader constructor.
* @param null $directory
*/
public function __construct($directory = null) {
if (is_null($directory)) {
$directory = $this->chercher_racine_spip();
}
$this->directory = rtrim(Files::formatPath($directory), DIRECTORY_SEPARATOR);
}
/**
* Cherche la racine d'un site SPIP
*
* Retourne le chemin absolu vers la racine du site SPIP dans
* lequel se trouve le répertoire courant. Retourne FALSE si l'on
* est pas dans l'arborescence d'un site SPIP.
*
* @return string|bool
* Retourne le chemin vers la racine du SPIP dans lequel on se trouve.
* Retourne false si on n'est pas dans l'arborescence d'une installation SPIP.
*/
private function chercher_racine_spip() {
$cwd = getcwd();
while ($cwd) {
if (file_exists(Files::formatPath($cwd . DIRECTORY_SEPARATOR . $this->starter))) {
return $cwd;
} else {
/* On remonte d'un dossier dans l'arborescence */
$cwd_array = explode(DIRECTORY_SEPARATOR, $cwd);
array_pop($cwd_array);
$cwd = implode(DIRECTORY_SEPARATOR, $cwd_array);
}
}
return false;
}
/**
* Indique si on est à la racine d’un site SPIP
* @return bool;
*/
public function exists() {
if (is_null($this->exists)) {
$this->exists = is_file($this->getPathFile($this->starter));
}
return $this->exists;
}
/**
* Démarre SPIP
*/
public function load() {
if ($this->loaded) {
return true;
}
if (!$this->exists()) {
throw new \Exception('SPIP has not been found in ' . $this->directory);
}
$starter = $this->getPathFile($this->starter);
$this->loaded = true;
$this->runSpipWithUglyGlobals($starter);
if (!defined('_ECRIRE_INC_VERSION')) {
throw new \Exception('SPIP is incorrectly loaded');
}
// Charger l'API SQL.
include_spip('base/abstract_sql');
return true;
}
public function getPathConnect() {
return $this->getPathFile($this->connect);
}
/**
* Retourne un chemin complet vers un fichier de SPIP
* @param string $path Chemin tel que 'ecrire/inc_version.php'
* @return string Chemin complet
*/
public function getPathFile($path) {
return $this->directory . DIRECTORY_SEPARATOR . Files::formatPath($path);
}
/**
* Déclarer les globales utilisées encore par SPIP.
* @param string $starter Chemin du fichier de démarrage de SPIP.
*/
public function runSpipWithUglyGlobals($starter) {
global
$nombre_de_logs,
$taille_des_logs,
$table_prefix,
$cookie_prefix,
$dossier_squelettes,
$filtrer_javascript,
$type_urls,
$debut_date_publication,
$ip,
$mysql_rappel_connexion,
$mysql_rappel_nom_base,
$test_i18n,
$ignore_auth_http,
$ignore_remote_user,
$derniere_modif_invalide,
$quota_cache,
$home_server,
$help_server,
$url_glossaire_externe,
$tex_server,
$traiter_math,
$xhtml,
$xml_indent,
$source_vignettes,
$formats_logos,
$controler_dates_rss,
$spip_pipeline,
$spip_matrice,
$plugins,
$surcharges,
$exceptions_des_tables,
$tables_principales,
$table_des_tables,
$tables_auxiliaires,
$table_primary,
$table_date,
$table_titre,
$tables_jointures,
$liste_des_statuts,
$liste_des_etats,
$liste_des_authentifications,
$spip_version_branche,
$spip_version_code,
$spip_version_base,
$spip_sql_version,
$spip_version_affichee,
$visiteur_session,
$auteur_session,
$connect_statut,
$connect_toutes_rubriques,
$hash_recherche,
$hash_recherche_strict,
$ldap_present,
$meta,
$connect_id_rubrique,
$puce;
// Éviter des notices. Il faudrait utiliser HTTPFondation\Request dans SPIP.
if (!$this->app['debug']) {
foreach (['SERVER_NAME', 'SERVER_PORT', 'REQUEST_METHOD', 'REQUEST_URI'] as $key) {
if (!isset($_SERVER[$key])) {
$_SERVER[$key] = null;
}
}
}
$cwd = getcwd();
chdir($this->directory);
$this->preparerPourInstallation();
require_once $starter;
chdir($cwd);
}
/**
* Hack : afin que le chargement de SPIP ne termine pas par une page "minipres"
* indiquer, si le SPIP n’a pas encore sa BDD de configurée, que l’on est sur la
* page d’installation...
*
*/
protected function preparerPourInstallation() {
// Si jamais la base n'est pas installé on anhile la redirection et on affirme qu'on est sur la page d'installation
if (!is_file($this->connect)) {
$_GET['exec'] = 'install';
define('_FILE_CONNECT', 'config/connect.tmp.php');
}
// TIMEOUT de 24h…
define('_UPGRADE_TIME_OUT', 24*3600);
}
/**
* Chemin vers le répertoire SPIP
* @return string
*/
public function getDirectory() {
return $this->directory;
}
/**
* Sets a pimple instance onto this application.
*
* @param Container $app
* @return void
*/
public function setContainer(Container $app) {
$this->app = $app;
}
}

156
src/Loader/Sql.php

@ -0,0 +1,156 @@
<?php
namespace Spip\Cli\Loader;
/**
* Base class for Polatouche commands.
* @api
*/
class Sql {
private $connect;
private $done = false;
private $infos = [];
private $pdo = null;
/**
* Sql constructor.
* @param null $connectPath Chemin du connect
*/
public function __construct($connectPath = null) {
if ($connectPath) {
$this->setConnect($connectPath);
}
}
/**
* Définit le chemin du connect
* @param string $connect
* @throws \Exception
*/
public function setConnect($connect) {
if (!is_file($connect)) {
throw new \Exception('Connect file not found : ' . $connect);
}
$this->done = false;
$this->pdo = null;
$this->connect = $connect;
}
/**
* Retourne une instance de Pdo chargé sur le connect du site SPIP
* @return \PDO
*/
public function getPdo() {
if (!$this->done) {
$infos = $this->getInfo();
$dsn = $this->getPdoDsn($infos);
$this->pdo = new \PDO($dsn, $infos['username'], $infos['password']);
}
return $this->pdo;
}
/**
* Retourne les informations du connect
* @return array|bool
*/
public function getInfo() {
if (!$this->done) {
$this->infos = $this->infoConnect($this->connect);
}
return $this->infos;
}
public function getPdoDsn($infos) {
$dsn = "";
if (!is_array($infos) or empty($infos['driver'])) {
throw new \Exception('Connect info needs driver name.');
}
switch ($infos['driver']) {
case 'mysql':
// $db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'username', 'password');
$params = [];
// en CLI on ne trouve pas toujours le bon socket pour localhost
foreach (['host', 'dbname', 'charset'] as $param) {
if (!empty($infos[$param])) {
$params[] = $param . '=' . $infos[$param];
}
}
$dsn = 'mysql:' . implode(';', $params);
break;
case 'sqlite':
case 'sqlite3':
// $db = new PDO('sqlite:chemin.sqlite');
$dsn = 'sqlite:' . $infos['file'];
break;
}
return $dsn;
}
/**
* Retourne la liste des paramètres d’un 'connect' SPIP
* @param string $connectFile Chemin du connect
* @return bool
*/
public function infoConnect($connect) {
if (!$connect or !is_file($connect)) {
throw new \Exception('Connect file not found : ' . $connect);
}
$content = file_get_contents($connect);
$content = explode("\n", $content);
$content = array_filter($content, function($line) {
if (strpos($line, 'spip_connect_db') === 0) {
return true;
}
return false;
});
$content = end($content);
if (!$content) {
throw new \Exception('Parsing connect file in error.');
}
$content = str_replace(['spip_connect_db', '(', ')'], '', rtrim(rtrim($content), ';'));
$content = explode(',', $content);
array_walk($content, function(&$value) {
$value = trim(trim(trim($value), "'"), '"');
});
if (!is_array($content)) {
throw new \Exception('Parsing connect file in error.');
}
if (count($content) !== 9) {
if (count($content) == 8) {
$content[] = 'utf8';
} else {
throw new \Exception('Incorrect argument count in connect file.');
}
}
$keys = [
'host',
'port',
'username',
'password',
'dbname',
'driver',
'spip_prefix',
'spip_auth',
'charset',
];
$infos = array_combine($keys, $content);
if (in_array($infos['driver'], ['sqlite', 'sqlite3'])) {
$infos['file'] = $this->findSqliteDatabaseFromConnect($infos['dbname'], $connect);
}
return $infos;
}
public function findSqliteDatabaseFromConnect($dbname, $connect) {
$sqlite = dirname($connect) . '/bases/' . $dbname . '.sqlite';
if (!is_file($sqlite)) {
throw new \Exception('Sqlite database not found : ' . $sqlite);
}
return $sqlite;
}
}

75
src/Tools/Files.php

@ -0,0 +1,75 @@
<?php
namespace Spip\Cli\Tools;
use Composer\Autoload\ClassLoader;
class Files {
/**
* Retourne un path avec tous les \ ou / remplacés par le séparateur attendu par l’OS
* @param string $path
* @return string mixed
*/
public static function formatPath($path) {
return str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
}
/**
* Find the root directory of a Composer application (ie: vendor/../)
* @return string;
* @throws \RuntimeException
* @throws \ReflectionException
*/
public static function findAppRootDirectory() {
static $root = null;