{"id":4407,"date":"2025-10-16T09:27:02","date_gmt":"2025-10-16T07:27:02","guid":{"rendered":"https:\/\/davidperezgar.com\/?p=4407"},"modified":"2025-10-21T12:20:49","modified_gmt":"2025-10-21T10:20:49","slug":"analiza-tu-codigo-automaticamente-con-phpstan","status":"publish","type":"post","link":"https:\/\/davidperezgar.com\/es\/analiza-tu-codigo-automaticamente-con-phpstan\/","title":{"rendered":"Analiza tu c\u00f3digo autom\u00e1ticamente con PHPSTAN"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En proyectos de WordPress, mantener un c\u00f3digo limpio y libre de errores es fundamental para garantizar la calidad, la seguridad y la escalabilidad. Herramientas como <strong>PHPStan<\/strong> (an\u00e1lisis est\u00e1tico) y <strong>PHPLint<\/strong> (validaci\u00f3n de sintaxis) se integran f\u00e1cilmente en tus procesos de <strong>testing automatizado<\/strong>, ayud\u00e1ndote a detectar problemas antes de que lleguen a producci\u00f3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta entrada aprender\u00e1s c\u00f3mo a\u00f1adir ambas herramientas en tu flujo de <strong>tests para WordPress<\/strong> paso a paso.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">\u00bfPor qu\u00e9 usar PHPStan?<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\"><strong>PHPStan<\/strong>: analiza tu c\u00f3digo sin ejecutarlo, detectando errores de tipos, llamadas a m\u00e9todos inexistentes y malas pr\u00e1cticas.<\/li>\n\n\n\n<li class=\"\">Ambas herramientas mejoran la <strong>calidad del c\u00f3digo<\/strong> y facilitan la <strong>colaboraci\u00f3n en equipos<\/strong> de desarrollo WordPress.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Instalaci\u00f3n con Composer<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Aseg\u00farate de tener instalado Composer en tu entorno de desarrollo. Luego a\u00f1ade las dependencias:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">composer require --dev phpstan\/phpstan wp-coding-standards\/wpcs szepeviktor\/phpstan-wordpress phpstan\/extension-installer\n<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Esto instalar\u00e1 ambas herramientas dentro de tu carpeta <code>vendor\/<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Configuraci\u00f3n de PHPStan<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">Crea el archivo <code>phpstan.neon.dist<\/code> en la ra\u00edz del proyecto (incluye WooCommerce):<\/li>\n<\/ol>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">parameters:\n    level: 1\n    paths:\n        - includes\n    bootstrapFiles:\n        - tests\/phpstan-bootstrap.php\n    excludePaths:\n        - vendor\/\n        - node_modules (?)<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Ejemplo phpstan<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">&lt;?php\n\/**\n * PHPStan Bootstrap File\n * \n * This file defines constants and functions that PHPStan needs to understand\n * but are not available during static analysis.\n *\/\n\n\/\/ Define plugin constants that are used throughout the codebase\nif (!defined('CONHOLD_PLUGIN_URL')) {\n    define('CONHOLD_PLUGIN_URL', 'http:\/\/localhost\/wp-content\/plugins\/connect-ecommerce\/');\n}\n\nif (!defined('CONHOLD_VERSION')) {\n    define('CONHOLD_VERSION', '1.0.0');\n}\n\nif (!defined('CONHOLD_FILE')) {\n    define('CONHOLD_FILE', __FILE__);\n}\n\n\/\/ Define WordPress constants that might be missing\nif (!defined('DOING_AJAX')) {\n    define('DOING_AJAX', false);\n}\n\nif (!defined('WP_DEBUG')) {\n    define('WP_DEBUG', false);\n}\n\nif (!defined('ABSPATH')) {\n    define('ABSPATH', '\/path\/to\/wordpress\/');\n}\n\n\/\/ Mock WordPress functions that PHPStan can't find\nif (!function_exists('wp_doing_ajax')) {\n    function wp_doing_ajax() {\n        return defined('DOING_AJAX') &amp;&amp; DOING_AJAX;\n    }\n}\n\nif (!function_exists('CONHOLD_get_options')) {\n    function CONHOLD_get_options() {\n        return [];\n    }\n}\n\n\n\/\/ Mock Action Scheduler function\nif (!function_exists('as_schedule_recurring_action')) {\n    function as_schedule_recurring_action($timestamp, $interval_in_seconds, $hook, $args = [], $group = '') {\n        return true;\n    }\n}\n\n\/\/ Mock WP_CLI class\nif (!class_exists('WP_CLI')) {\n\tclass WP_CLI {\n\t\t\tpublic static function line($message) {\n\t\t\t\t\techo $message . \"\\n\";\n\t\t\t}\n\t\t\tpublic static function add_command($command, $class) {\n\t\t\t\t\treturn true;\n\t\t\t}\n\t}\n}<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Configura <code>composer.json<\/code>:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">\"scripts\": {\n\t\"format\": \"phpcbf --standard=phpcs.xml.dist\",\n\t\"lint\": \"phpcs --standard=phpcs.xml.dist\",\n\t\"phpstan\": \"phpstan analyse --memory-limit=2048M\"\n}\n<\/code><\/pre><\/div>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li class=\"\">Ejecuta el an\u00e1lisis:<\/li>\n<\/ol>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">composer phpstan<\/code><\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Configuraci\u00f3n con WooCommerce<\/h2>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">composer require --dev php-stubs\/wordpress-stubs php-stubs\/woocommerce-stubs<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Y a\u00f1adir el include en el archivo de configuraci\u00f3n<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">parameters:\n    bootstrapFiles:\n        - vendor\/php-stubs\/wordpress-stubs\/wordpress-stubs.php\n        - vendor\/php-stubs\/woocommerce-stubs\/woocommerce-stubs.php\n        #- vendor\/php-stubs\/woocommerce-stubs\/woocommerce-packages-stubs.php<\/code><\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Configuraci\u00f3n de PHPLint<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">Crea un archivo <code>.phplint.yml<\/code>:<\/li>\n<\/ol>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">path: .\njobs: 10\nextensions:\n  - php\n<\/code><\/pre><\/div>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li class=\"\">Ejecuta el an\u00e1lisis:<\/li>\n<\/ol>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">composer lint<\/code><\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Integraci\u00f3n con PHPUnit y GitHub Actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Puedes a\u00f1adir ambos chequeos en tus <strong>pipelines de CI\/CD<\/strong> para que cada commit o pull request pase por estas validaciones.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Y a\u00f1adir el archivo con tus propias reglas de Coding Standards:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">&lt;?xml version=\"1.0\"?&gt;\n&lt;ruleset name=\"Coding Standards for Internal Scanner Tool\"&gt;\n\t&lt;description&gt;Description of Plugin.&lt;\/description&gt;\n\n\t&lt;exclude-pattern&gt;*\/vendor\/*&lt;\/exclude-pattern&gt;\n\t&lt;exclude-pattern&gt;reports\/*&lt;\/exclude-pattern&gt;\n\t&lt;exclude-pattern&gt;php\/tests\/*&lt;\/exclude-pattern&gt;\n\t&lt;exclude-pattern&gt;php\/prt_phpunit\/*&lt;\/exclude-pattern&gt;\n\n\t&lt;arg value=\"ps\"\/&gt;\n\t&lt;arg name=\"extensions\" value=\"php\"\/&gt;\n\n\t&lt;file&gt;.\/plugin.php&lt;\/file&gt;\n\t&lt;file&gt;.\/includes&lt;\/file&gt;\n\t&lt;file&gt;.\/templates&lt;\/file&gt;\n\n\t&lt;rule ref=\"WordPress\"&gt;\n\t\t&lt;exclude name=\"Universal.Arrays.DisallowShortArraySyntax\"\/&gt;\n\t\t&lt;exclude name=\"WordPress.DB\" \/&gt;\n\t\t&lt;exclude name=\"WordPress.Files.FileName\"\/&gt;\n\t\t&lt;exclude name=\"WordPress.NamingConventions.ValidFunctionName\" \/&gt;\n\t&lt;\/rule&gt;\n\n\t&lt;rule ref=\"WordPress.WP.I18n\"&gt;\n\t\t&lt;properties&gt;\n\t\t\t&lt;property name=\"text_domain\" type=\"array\" value=\"plugin-name\" \/&gt;\n\t\t&lt;\/properties&gt;\n\t&lt;\/rule&gt;\n\n\t&lt;rule ref=\"WordPress.Utils.I18nTextDomainFixer\"&gt;\n\t\t&lt;properties&gt;\n\t\t\t&lt;property name=\"old_text_domain\" type=\"array\"&gt;\n\t\t\t\t&lt;element value=\"\" \/&gt;\n\t\t\t&lt;\/property&gt;\n\t\t\t&lt;property name=\"new_text_domain\" value=\"plugin-name\" \/&gt;\n\t\t&lt;\/properties&gt;\n\t&lt;\/rule&gt;\n&lt;\/ruleset&gt;<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">De este modo, al ejecutar <code>composer format<\/code> o composer lint, se correr\u00e1n PHPUnit, PHPStan y PHPLint juntos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A\u00f1adir en .github\/workflows\/php-lint.yml<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">name: PHP Code Linting\n\non:\n  push:\n    branches:\n      - trunk\n      - 'release\/**'\n    # Only run if PHP-related files changed.\n    paths:\n      - '.github\/workflows\/php-lint.yml'\n      - '**.php'\n      - 'phpcs.xml.dist'\n      - 'composer.json'\n      - 'composer.lock'\n  pull_request:\n    branches:\n      - trunk\n      - 'release\/**'\n      - 'feature\/**'\n    # Only run if PHP-related files changed.\n    paths:\n      - '.github\/workflows\/php-lint.yml'\n      - '**.php'\n      - 'phpcs.xml.dist'\n      - 'composer.json'\n      - 'composer.lock'\n    types:\n      - opened\n      - reopened\n      - synchronize\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs\/heads\/trunk' }}\njobs:\n  php-lint:\n    name: PHP\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n    steps:\n      - uses: actions\/checkout@v4\n\n      - uses: shivammathur\/setup-php@v2\n        with:\n          php-version: '8.0'\n\n      - name: Validate Composer configuration\n        run: composer validate\n\n      - name: Install PHP dependencies\n        uses: ramsey\/composer-install@a2636af0004d1c0499ffca16ac0b4cc94df70565\n        with:\n          composer-options: '--prefer-dist'\n\n      - name: PHP Lint\n        run: composer lint\n\n      - name: PHP PHPStan\n        run: composer phpstan<\/code><\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusi\u00f3n<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Integrar <strong>PHPStan y PHPLint en tus TESTs de WordPress<\/strong> es una manera sencilla de elevar la calidad del desarrollo, detectar errores de forma temprana y mejorar la robustez de tus plugins y temas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si quieres llevar tu proyecto al siguiente nivel, empieza por a\u00f1adir estas herramientas en tu flujo de trabajo. \ud83d\ude80\ud83d\udc49 \u00bfQuieres que te prepare este art\u00edculo <strong>en formato markdown completo y listo para publicar en WordPress<\/strong> (con encabezados <code>h2<\/code>, <code>h3<\/code>, bloques de c\u00f3digo y enlaces a las herramientas), o prefieres que lo deje en texto plano para que lo adaptes t\u00fa?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Referencias:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">PHPStan en WordPress <a href=\"https:\/\/pascalbirchler.com\/phpstan-wordpress\/\" target=\"_blank\" rel=\"noopener\">https:\/\/pascalbirchler.com\/phpstan-wordpress\/<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>En proyectos de WordPress, mantener un c\u00f3digo limpio y libre de errores es fundamental para garantizar la calidad, la seguridad y la escalabilidad. Herramientas como PHPStan (an\u00e1lisis est\u00e1tico) y PHPLint (validaci\u00f3n de sintaxis) se integran f\u00e1cilmente en tus procesos de testing automatizado, ayud\u00e1ndote a detectar problemas antes de que lleguen a producci\u00f3n. En esta entrada [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":12,"footnotes":""},"categories":[168],"tags":[],"class_list":["post-4407","post","type-post","status-publish","format-standard","hentry","category-desarrollo-web"],"_links":{"self":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts\/4407","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/comments?post=4407"}],"version-history":[{"count":1,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts\/4407\/revisions"}],"predecessor-version":[{"id":5160,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts\/4407\/revisions\/5160"}],"wp:attachment":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/media?parent=4407"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/categories?post=4407"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/tags?post=4407"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}