Documentación · Referencia
La biblioteca PHP y la orden de consola
Lo que se promete de la biblioteca PHP y de la orden ehundu.
Versión 0.16
Ehundu se versiona con versionado semántico (se abre en una pestaña nueva) y la
promesa de una versión mayor cubre tres cosas: el formato de un proyecto
(formato.md), la orden de consola y lo que este documento
llama la biblioteca pública. Dentro de una versión mayor solo se añade; quitar
algo o cambiar su significado es una versión mayor, y lo que va a dejar de
valer avisa durante, al menos, una versión menor antes de quitarse
(formato.md, §12, reglas 5 y 6).
Lo que no está aquí no se promete, aunque PHP lo deje usar. Las demás clases
de src/ llevan @internal y pueden cambiar o desaparecer en cualquier
versión; de las que están aquí, solo se promete lo que se nombra, y no
cualquier otro método o propiedad pública (Sitio es público, pero no
Sitio::$encabezados). Los nombres de los parámetros de lo que se promete son
parte de la API, porque se pueden pasar por nombre (segundos:, compilado:).
Los textos de los avisos y de los errores no se prometen: están pensados para que
los lea una persona.
La orden de consola¶
ehundu compilar [carpeta]
ehundu servir [carpeta] [--puerto=8000] [--borradores] [--completo]
ehundu desplegar [carpeta] [--simular] [--todo]
ehundu --ayuda
ehundu --version
Las órdenes, sus opciones y lo que hacen son los de ehundu --ayuda; -h y
--help son lo mismo que --ayuda, y -V, lo mismo que --version.
ehundu --version escribe Ehundu y el número de la versión, y sale con 0. El
código de salida es 0 si todo ha ido bien, 1 si el proyecto no se puede
compilar o desplegar, y 2 si la orden está mal escrita o no hay ninguna (entonces
escribe la ayuda en el error estándar). Un fallo del propio motor, que no es un
error de proyecto, sale con el 255 de PHP. Se prometen los nombres y el efecto,
no el texto que escriben en la terminal.
La biblioteca¶
Todas viven en el espacio de nombres Ehundu\ (el de Despliegue, en
Ehundu\Despliegue\). La lista sale de lo que usa el primer programa que las
incrusta, un editor web.
Compilar¶
Compilador(interfaz).compilar(Proyecto $proyecto): Informe. Otros programas pueden implementarla (elDesplegadoracepta cualquierCompilador), y por eso no crece dentro de una versión mayor: lo que falte vendría como una interfaz aparte. Lo mismo vale paraDespliegue\Destino.CompiladorEnProcesoimplementaCompilador: compila el proyecto entero y lo escribe ensalida/, sin depender de la consola, para llamarlo desde un proceso web.__construct(?\DateTimeImmutable $ahora = null)fija el momento con el que se decide qué está publicado. LanzaErrorDeProyectosi el proyecto no se puede compilar y no tocasalida/si falla la lectura.Informees lo que devuelve:$paginas,$ficheros,$segundos,$avisos(una lista deAviso),$rehechasyresumen(), una frase.$rehechases siempre un número: las páginas que se han construido de nuevo, que en una compilación completa son todas ($paginas). Un programa puede construir uno:Informe(paginas:, avisos:, segundos:, ficheros: 0, rehechas: null), yrehechassin decir valepaginas.
CompiladorPorLotes y CompiladorPorLotesEnProceso, la compilación por lotes de
la 0.12 (formato.md, §10.4), no se prometen: son @internal
(decisión 128). Para repartir una compilación en llamadas con un presupuesto de
tiempo se usa avanzar, en la compilación incremental (más abajo).
Compilar de forma incremental¶
Para un proceso web que compila el mismo sitio una y otra vez, sobre todo si es
grande (formato.md, §10.5). Lo que recuerda entre una compilación
y la siguiente se guarda en .ehundu/memoria.bin, en el proyecto: un cambio en
una página rehace unas pocas, aunque la compilación anterior fuera de otro
proceso, y el resultado es el de una compilación completa, salvo en lo que no
depende del proyecto: random() y la fecha del momento ('now'|date) de una
página que no se rehace se quedan como estaban (compilacion.md).
CompiladorIncrementalEnProcesoimplementaCompilador.__construct(?\DateTimeImmutable $ahora = null)fija el momento con el que se decide qué está publicado.compilar(Proyecto): Informecompila de una vez (si otra llamada está compilando el proyecto, espera a que termine, como mucho un minuto; pasado ese tiempo lanzaErrorDeProyecto, y lo mismo haceolvidar);$informe->rehechasdice cuántas páginas se han rehecho. Sin una memoria que valga (la primera vez, tras cambiar de versión del motor, o si se borra.ehundu/) hace una compilación completa. LanzaErrorDeProyectosi el proyecto no se puede compilar, y entonces deja la memoria ysalida/como estaban. Si la memoria guardada no cabe en elmemory_limitde PHP (pide unas tres veces lo que mide el fichero más 16 MB), compila entero sin usarla ni tocarla y da un aviso que dice a cuánto subir el límite.avanzar(Proyecto, float $segundos): Lotehace lo mismo con un presupuesto: rehace páginas hasta agotarlo, y siempre al menos una, guarda lo hecho y devuelve unLoteque dice si está completo. La llamada siguiente sigue donde se quedó, también si el proyecto ha cambiado entretanto: solo se rehace lo que el cambio afecta. Hasta que elLoteestá completo,salida/mezcla páginas nuevas y viejas, y nada debe publicarse. Una llamada que llega mientras otra está avanzando no espera: devuelveocupado(). Si la memoria guardada no cabe en elmemory_limitde PHP, lanzaErrorDeProyectocon lo que ocupa, lo que pide y a cuánto subir el límite, porque sin ella no puede repartir el trabajo.olvidar(Proyecto)borra la memoria: la siguiente compilación será completa.Lote, lo que devuelveavanzar:$hechas,$total,completo(),progreso()(de 0 a 1; 0 en un loteocupado()),informe()(unInforme, cuando está completo) yocupado()(si otra llamada estaba avanzando y esta no ha hecho nada).$hechasy$totalcuentan páginas enavanzar; para saber cómo va una compilación, lo que se promete esprogreso()ycompleto(), y los números sueltos son de quien devuelve elLote.
El presupuesto de avanzar ($segundos), igual en el Desplegador, se cuenta
desde que empieza la llamada. Uno negativo o que no es un número (NAN) vale 0,
que sigue haciendo una tarea, la mínima; y uno de más de un día, un día, de modo
que INF o PHP_INT_MAX quieren decir «sin límite». El presupuesto incluye la
preparación de la llamada (cargar la memoria, leer el proyecto, decidir qué se
rehace), y la llamada puede acabar después de él: guardar la memoria viene tras
el último trabajo. Con un presupuesto menor que la preparación, cada llamada
rehace una sola página y vuelve a guardar la memoria entera: acaba, pero cada vez
más despacio. Un presupuesto útil es varias veces la preparación.
La memoria pesa del orden de salida/ más lo leído (97 MB en un sitio de 1.200
páginas) y se carga en una fracción de segundo; el proceso que la usa necesita
memoria de PHP para ella: unas tres veces lo que mide el fichero más 16 MB como
mucho (en ese sitio, entre 170 y 250 MB según lo que se rehaga; las medidas, en
compilacion.md). La cifra no se promete; el resguardo del memory_limit sí.
Leer un proyecto¶
Proyecto:Proyecto::abrir(string $ruta),$raiz,ruta(string ...$partes)(una ruta absoluta dentro del proyecto, con/),leerTexto(string $fichero)(sin BOM, con\npor final de línea, y exige UTF-8: si no, lanzaErrorDeProyecto),ficherosDe(string $carpeta)(los ficheros de esa carpeta y de sus subcarpetas, también los que empiezan por punto, con rutas relativas a ella y ordenadas byte a byte), y las constantes con los nombres de las carpetas y ficheros del formato:CONTENIDO,DATOS,PLANTILLAS,PARCIALES,PUBLICO,SALIDAySITIO.Lector:leer(Proyecto): Lectura, el proyecto entero sin compilar, yleerSitio(Proyecto, ?Avisos = null): Sitio, solositio.yml.leerda las páginas tal como están escritas: las que usanagruparopaginarsalen sin expandir, y una plantilla de grupos tiene$url === false, como un fragmento. SinAvisos,leerSitiopierde los avisos sin decirlo.Lectura:$sitio,$datos,$paginas(una lista dePagina, ordenadas por ruta),$avisosydatosDe(string $idioma), los datos de una página de ese idioma.Sitio:$nombre,$url,$zonaHoraria,$campos(todositio.ymlsalvodespliegue:urlsin la barra final, yidiomaeidiomas, la lista de{codigo, nombre, url}, siempre, aunque el fichero no los declare; es la única vía pública a los idiomas del sitio),$formatoyabsoluta(string $url), la dirección completa de una URL del sitio.Pagina:$ruta,$formato(mdotwig),$campos,$url(ofalseen un fragmento),$cuerpo,$lineaCuerpo,$idioma,clave()(lo que comparte con sus traducciones),valor(string $campo),urlPdf()yestaPublicada(\DateTimeImmutable $ahora, bool $conBorradores = false).$camposes lo que queda de la cascada (el_datos.yml, el.ymlcomún de la página y su front matter, formato §3) como lo deja el motor (formato §4): los nombres ingleses pasan a su nombre en español;fechaypublicarson\DateTimeImmutable(el día, a medianoche, en la zona del sitio), y un día sin hora de cualquier otro campo también;borrador,listada,pdfypublicarEnSuFechasonbool;ordenes un número;titulo,subtitulo,descripcion,imagen,imagenAlteiconoson siempre texto;etiquetas,cssyjsson listas de textos;plantillaes un texto ofalse;urles un texto ofalse, el patrón o la dirección tal como se escribió (la ya resuelta es$url);agruparypaginarsalen normalizados como mapas; un campo en blanco está, comonull; el resto, como lo lee YAML. Un valor que no valía se avisó y no está.valor()da además los nombres que escribe el motor (url,ruta,idioma,pdf: la dirección del PDF onull),nullde un campo que no existe, y de unaurlque esfalse,null.Lectura::$datosson los ficheros dedatos/tal como los leen YAML y JSON.Formato:VERSION, la versión del formato que entiende esta versión de Ehundu, yleer(mixed $valor, string $fichero, ?int $linea): int, que comprueba la que declara un proyecto o un esquema (formato.md, §2): un valor que no es un entero mayor que cero, o es mayor queVERSION, lanzaErrorDeProyecto; una clave ausente (null) da1, y un decimal (1.0) o un texto ("1") lanzan. El motor la usa consitio.yml; no lee el esquema (§11), pero un editor puede usarla con el suyo.Slug:Slug::de(string $texto), el filtroslugdel formato (§5.2). Los demás métodos deSlugno se prometen.Version:Version::ACTUAL, la versión de Ehundu como texto:0.15.0en una publicada y0.16.0-deventre una y otra. Es la que escribeehundu --version. No es la versión del formato (Formato::VERSION).
Avisos y errores¶
Aviso:$mensaje,$fichero,$lineay__toString(), con la ubicación delante. Un programa puede construir uno:Aviso(mensaje:, fichero: null, linea: null).Avisos:registrar(string $mensaje, ?string $fichero = null, ?int $linea = null),escuchar(\Closure $oyente)ytodos().escucharsustituye al oyente anterior: solo hay uno, y recibe todos los avisos según se registran, también los repetidos, quetodos()junta en uno.ErrorDeProyecto, una\RuntimeExceptioncon$descripcion,$ficheroy$linea: lo que impide compilar, leer o desplegar un proyecto. Un programa que implementeCompiladoroDestinolo lanza conErrorDeProyecto(string $descripcion, ?string $fichero = null, ?int $linea = null, ?\Throwable $anterior = null).
Lo que lanza la biblioteca es ErrorDeProyecto, con tres excepciones que se
prometen: Configuracion::leer lanza \InvalidArgumentException si en
$secretos hay una clave que no es clave, clavePrivada ni frase;
Manifiesto::leer, \UnexpectedValueException si el texto no es un manifiesto
o una de sus rutas sale del sitio; y Desplegador::desplegar y ejecutar
vuelven a lanzar, después de guardar el manifiesto, lo que lance un Destino que
no es de Ehundu. Cualquier otra excepción (un \TypeError, por ejemplo) es un fallo del
motor y sale tal cual.
Desplegar¶
Desplegador hace lo que ehundu desplegar (despliegue.md);
las demás clases son las piezas con las que se compone, para un programa que
quiera llevar el despliegue a su manera, como hace un editor web que las usa.
Despliegue\Desplegador:__construct(Compilador $compilador = ..., ?\Closure $abrir = null)($abrires para las pruebas y no se promete),desplegar(Proyecto $proyecto, bool $simular = false, bool $todo = false, array $secretos = [], ?\Closure $alAvanzar = null, ?\Closure $alCompilar = null, ?\Closure $alAvisar = null, ?float $segundos = null, ?Informe $compilado = null), que devuelve unInformeDeDespliegue, yejecutar(PlanDeDespliegue $plan, Proyecto $proyecto, Destino $destino, ?Manifiesto $anterior, ?\Closure $alAvanzar = null, ?float $segundos = null): bool, que sube un plan ya trazado y dice si lo ha hecho entero. Los nombres de los parámetros forman parte de la API, porque se pasan por nombre (segundos:,compilado:):alAvanzarse llama con «subido» o «borrado» y la ruta, según se va haciendo;alCompilar, con elInformede la compilación antes de conectar con el destino; yalAvisar, con cadaAvisosegún sale. Con$segundos, al agotarse guarda el manifiesto y para (siempre hace al menos un fichero), y la llamada siguiente sigue donde se quedó; con$compilado, elInformede una compilación ya hecha (repartida conavanzar, por ejemplo), no vuelve a compilar. Con$todose ignora$segundosy se sube todo en una llamada: repartido, cada llamada empezaría de cero y no terminaría nunca.Despliegue\Configuracion:Configuracion::leer(Proyecto $proyecto, array $secretos = [], Avisos $avisos = new Avisos()), la seccióndesplieguedesitio.ymlcon sus secretos ($clavePrivadaes la ruta de un fichero, no su contenido: relativa al proyecto, absoluta o con~/); sus propiedades ($destino,$servidor,$puerto,$usuario,$ruta,$huella,$cifrado,$region,$cubo,$clave,$clavePrivaday$frase),descripcion(), sin secretos, yabrir(Proyecto $proyecto, Avisos $avisos): Destino, que abre el destino que dice la configuración (Carpetacrea su carpeta al subir el primer fichero, no antes, así que simular no deja nada). Unvar_dumpno enseña ni los secretos ni dónde está la clave privada.Despliegue\Manifiesto: lo que Ehundu subió, que vive en el destino (.ehundu.json):FICHERO,$url,leer(string $json),ficheros(),anotar(string $ruta, string $md5, int $tamano),quitar(string $ruta)yjson(). Un programa puede construir uno:Manifiesto(ficheros: [], url: null). Un Ehundu 1.x lee el manifiesto que dejó cualquier versión anterior (formato.md, §10).Despliegue\PlanDeDespliegue: qué subir y qué borrar:PlanDeDespliegue::trazar(array $ficheros, ?Manifiesto $anterior, bool $todo = false, ?string $url = null),$subidas,$borrados,$sinCambios,$url,bytes(),estaVacio()ycarpetasQueSobran(array $quedan).PlanDeDespliegue::deSalida(Proyecto, ?Manifiesto $anterior = null, bool $todo = false, ?string $url = null)traza el plan de lasalida/de un proyecto ya compilado, yPlanDeDespliegue::ficheros(Proyecto)da los ficheros desalida/con su MD5 y su tamaño, que es lo que se compara con el manifiesto: quien llama no tiene que calcularlos.Despliegue\InformeDeDespliegue:$compilacion(unInforme),$destino,$subidos,$bytes,$borrados,$sinCambios,$avisos,$segundos,$simulado,$completo(falso si se ha parado porque se agotó el tiempo),$pendientes(cuántos ficheros quedan, entonces) yresumen().Despliegue\Destino(interfaz): a dónde se sube.leer(string $ruta): ?stringda el contenido de un fichero, onullsi no existe;subir(string $ruta, string $origen): voidcopia un fichero local;escribir(string $ruta, string $contenido): void;borrar(string $ruta): void, que no hace nada si no existe;quitarCarpeta(string $ruta): void, que no hace nada si no está vacía; ycerrar(): void. Las rutas son relativas al destino y sin barra al principio, y un fallo es unErrorDeProyecto. Un programa puede implementarla para llevar el sitio a un destino que Ehundu no trae.Despliegue\Carpeta,Despliegue\Ftp,Despliegue\SftpyDespliegue\S3implementanDestino. Se abren conConfiguracion::abrir: sus constructores no se prometen, y los parámetros que traen para las pruebas tampoco.