Tipos estrictos en todas partes
Spec: ISO 32000-2, §7.5.5 ISO 32000-2 §7.5.5 Evidence: Code-backed PHPStan: Level 10, no src baseline
De un vistazo
Sección titulada «De un vistazo»NextPDF ejecuta PHPStan en Level 10 sobre el código fuente del motor, sin línea base de supresiones. Esta página explica por qué «sin línea base» es una decisión de diseño y no un detalle de las herramientas, y qué aporta en realidad ese rigor a un flujo cuyo cometido irrenunciable es no gestionar mal los datos.
Por qué esto importa
Sección titulada «Por qué esto importa»En la mayoría de las aplicaciones, el tipado estricto es higiene. En un motor de PDF se acerca más a un mecanismo de correctitud. El formato no perdona. Se espera que un lector localice el contenido leyendo el archivo desde el final, a través del tráiler y la tabla de referencias cruzadas, por lo que los desplazamientos de bytes del escritor deben ser exactos. Piénsese en un tipo que se ensancha sin avisar a mixed, un int que se convierte silenciosamente en un string, o un valor anulable que se desreferencia sin comprobar. Cualquiera de estos casos puede producir un archivo que se abre sin problemas en un visor y no supera la validación en otro, semanas después, sin ninguna traza de pila que señale la causa.
En este dominio, los fallos costosos son los silenciosos. El tipado estricto, sumado a un analizador estricto, es la forma en que el motor convierte una clase de fallos silenciosos en tiempo de ejecución en fallos ruidosos en tiempo de compilación.
La versión breve
Sección titulada «La versión breve»- El código fuente del motor se analiza en PHPStan Level 10 —el nivel más estricto— verificado en
phpstan.neon.dist. - No existe ninguna línea base de supresión del código fuente. La configuración fija el análisis del código fuente en cero errores. Una regresión hace fallar la compilación en lugar de quedar absorbida en un archivo de exclusiones cada vez mayor.
- Las pocas entradas de
ignoreErrorsque existen son estrechas, acotadas por identificador y por ruta, y justificadas individualmente en la configuración (límites de dependencia blanda entre paquetes y costuras de prueba orientadas a la reflexión), no una línea base masiva. - Un perfil estricto independiente ejecuta
level: maxy prohíbe cualquier entrada de exclusión nueva, de modo que el código nuevo se mantiene bajo un umbral aún más exigente. - El efecto buscado es la presión de diseño: el código que no puede expresarse con honestidad de tipos no pasa, así que se rediseña en lugar de suprimirse.
Cómo lo aborda NextPDF
Sección titulada «Cómo lo aborda NextPDF»La diferencia entre «usamos un analizador estricto» y «usamos un analizador estricto sin línea base» es precisamente el quid de la cuestión, así que conviene ser preciso.
Una línea base registra cada infracción existente y le indica al analizador que ignore exactamente esas infracciones. Es una forma pragmática de adoptar el análisis estático en una base de código heredada, pero tiene un costo. La línea base se convierte en un registro silencioso de deuda que el sistema de tipos ha acordado no mirar. Las infracciones nuevas del mismo tipo pueden colarse junto a las heredadas. La promesa del analizador se debilita de «este código está limpio de tipos» a «este código no está peor de lo que estaba».
NextPDF no acepta ese acuerdo para el código fuente del motor. La configuración fija el análisis del código fuente en cero errores y activa reportUnmatchedIgnoredErrors, de modo que incluso una supresión obsoleta —una que ya no coincide con nada— hace fallar la compilación. Las exclusiones estrechas que permanecen están acotadas a un identificador de error y un archivo específicos. Cada una lleva una explicación en línea de por qué el límite es intencionado (por ejemplo, cuando el núcleo se programa contra una interfaz Pro/Enterprise de la que deliberadamente no tiene una dependencia concreta). Un revisor puede leer cada una y juzgarla. No hay ninguna lista opaca cuyo rastro pueda perderse.
El flujo que mantiene honesta esta disciplina:
- Change proposed New or modified engine code.
- Level 10 analysis Strictest PHPStan level over src/, treatPhpDocTypesAsCertain on.
- Zero-error gate No source baseline; unmatched ignores also fail.
- Strict profile level: max; no new ignore entries permitted.
- Redesign, not suppress If it cannot be expressed honestly, the design changes.
treatPhpDocTypesAsCertain forma parte de esto. Las anotaciones de PHPDoc se tratan como fuente de verdad, así que un @param list<T> o @return non-empty-string no es un comentario que el analizador pueda ignorar sin más. Es una promesa verificada. La anotación y el tipo en tiempo de ejecución quedan obligados a coincidir.
Qué dice la evidencia
Sección titulada «Qué dice la evidencia»Esta página está respaldada por Evidence: Code-backed . La configuración es la evidencia:
phpstan.neon.distestablecelevel: 10,phpVersion: 80400, analizasrcy no contiene ninguna clavebaseline:: no existe ningúnphpstan-baseline.neonpara analizar el código fuente.- El mismo archivo establece
treatPhpDocTypesAsCertain: trueyreportUnmatchedIgnoredErrors: true, con una nota en línea que indica que el análisis L10 del código fuente está fijado en cero errores y que cualquier regresión debe hacer fallar la CI. - Cada una de las entradas restantes de
ignoreErrorsestá acotada poridentifiery a menudo porpath, con comentarios que explican la justificación de dependencia blanda y de objetivo de reflexión: no son una línea base generada de forma masiva. phpstan-strict.neon.disthereda esa configuración, eleva el nivel amaxy congela la lista de exclusiones de modo que no pueda añadirse ninguna entrada nueva bajo el perfil estricto.
Desde el punto de vista normativo, la relación es directa. El motor debe producir archivos que un lector pueda navegar desde el tráiler y la tabla de referencias cruzadas según Spec: ISO 32000-2, §7.5.5 ISO 32000-2 §7.5.5 . La exactitud de los desplazamientos de bytes es un problema de tipos antes que un problema de serialización. Un desplazamiento es un entero que nunca debe convertirse silenciosamente en otra cosa. Un flujo limpio de tipos en Level 10 ya ha eliminado la mayoría de las formas en que la aritmética puede fallar en silencio.
Ejemplo práctico
Sección titulada «Ejemplo práctico»El tipado estricto es más visible cuando una regla de dominio se codifica como un tipo en lugar de como una comprobación en tiempo de ejecución. El discriminador de conformidad resuelve preguntas de nivel de especificación con un match exhaustivo, de modo que un caso no gestionado es un error de tipos, no un PDF incorrecto:
declare(strict_types=1);
enum ConformanceMode: string{ case Plain = 'plain'; case PdfUa2 = 'pdfua2'; case PdfA4 = 'pdfa4';
/** @return 2|3|4|null */ public function pdfaPart(): ?int { return match ($this) { self::PdfA4 => 4, default => null, }; }}El @return 2|3|4|null no es documentación. Con
treatPhpDocTypesAsCertain, se verifica. Quien lo llame asumiendo que el resultado es siempre un int recibirá una advertencia en tiempo de análisis, antes de que se escriba un solo byte de un número de parte de PDF/A no conforme.
Concepto erróneo habitual
Sección titulada «Concepto erróneo habitual»La trampa es leer «sin línea base» como «el código no tiene infracciones por casualidad». Eso está al revés. La ausencia de una línea base es la causa, no un resultado afortunado. Como no hay ningún lugar donde alojar una infracción, el código que produciría una tiene que escribirse de otra manera. Level 10 sin línea base de código fuente es una restricción que da forma al diseño, no un informe de evaluación que lo describe a posteriori.
Un segundo concepto erróneo: que el puñado de entradas de ignoreErrors es una línea base con otro nombre. No lo son. Una línea base se genera de forma masiva y es opaca. Estas se escriben individualmente, están acotadas por identificador, se explican y están protegidas por reportUnmatchedIgnoredErrors para que no puedan quedar obsoletas sin que nadie lo note.
Límites y fronteras
Sección titulada «Límites y fronteras»Esta página trata sobre el análisis del código fuente del motor. El conjunto de pruebas se analiza bajo un alcance y una configuración separados y deliberadamente distintos; «sin línea base» aquí es una afirmación sobre src/, no la afirmación de que todo análisis auxiliar del repositorio esté libre de línea base. PHPStan demuestra la solidez de tipos, no la correctitud del comportamiento. No sustituye a la pirámide de pruebas: solo elimina una categoría de fallos que, de lo contrario, las pruebas tendrían que perseguir. El nivel exacto, los indicadores y el conjunto de exclusiones son precisos a fecha de la revisión de esta página. La fuente autorizada es siempre phpstan.neon.dist y phpstan-strict.neon.dist en el repositorio principal.
La edición del producto no cambia esta disciplina. Cada edición se construye a partir del mismo código fuente de Level 10:
| Edition | Availability |
|---|---|
| Core | El código fuente del núcleo se analiza en Level 10 sin línea base para el código fuente. |
| Pro | Pro se construye sobre la misma disciplina de código fuente de Level 10. |
| Enterprise | Enterprise se construye sobre la misma disciplina de código fuente de Level 10. |
Documentos relacionados
Sección titulada «Documentos relacionados»- Los fundamentos de PHP 8.4: las características del lenguaje en las que se apoya el sistema de tipos.
- Los errores como característica: qué ocurre con los fallos que hace aflorar el tipado estricto.
- El modelo de canalización: la arquitectura que esta disciplina protege.
Glosario
Sección titulada «Glosario»- PHPStan Level 10: el nivel de análisis más estricto, que trata los valores sin tipar y con tipado laxo como errores en lugar de como advertencias.
- Línea base (baseline): un registro generado de infracciones existentes que se indica al analizador que ignore. NextPDF no usa ninguna para el código fuente del motor.
treatPhpDocTypesAsCertain: un ajuste de PHPStan que trata las anotaciones de tipo de PHPDoc como hechos verificados, no como comentarios orientativos.reportUnmatchedIgnoredErrors: un ajuste que hace fallar la compilación cuando una entrada de exclusión ya no coincide con nada, evitando supresiones obsoletas.- Presión de diseño: el efecto de una restricción que obliga a escribir el código de una manera determinada, en contraposición a una comprobación que solo lo mide.