{"id":13534042,"url":"https://github.com/Atanamo/PHP-Codeshift","last_synced_at":"2025-04-01T22:31:11.204Z","repository":{"id":60774195,"uuid":"127982384","full_name":"Atanamo/PHP-Codeshift","owner":"Atanamo","description":"A small PHP toolkit for running codemods (code transformations) over multiple PHP files.","archived":false,"fork":false,"pushed_at":"2022-10-23T19:41:41.000Z","size":45,"stargazers_count":29,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-24T00:43:46.879Z","etag":null,"topics":["automated","codemod","parser","php","refactoring","transform"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Atanamo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-03T23:57:28.000Z","updated_at":"2024-02-03T13:56:19.000Z","dependencies_parsed_at":"2022-10-04T15:24:06.778Z","dependency_job_id":null,"html_url":"https://github.com/Atanamo/PHP-Codeshift","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Atanamo%2FPHP-Codeshift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Atanamo%2FPHP-Codeshift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Atanamo%2FPHP-Codeshift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Atanamo%2FPHP-Codeshift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Atanamo","download_url":"https://codeload.github.com/Atanamo/PHP-Codeshift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246720487,"owners_count":20822913,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automated","codemod","parser","php","refactoring","transform"],"created_at":"2024-08-01T07:01:25.681Z","updated_at":"2025-04-01T22:31:10.938Z","avatar_url":"https://github.com/Atanamo.png","language":"PHP","readme":"\nPHP-Codeshift\n=============\n\nPHP-Codeshift* is a lightweight wrapper for the excellent library [PHP-Parser](https://github.com/nikic/PHP-Parser).\nIt mainly provides an easy-to-use API for running own codemod definition files on multiple PHP source files.\n\n\\* Yes, the name is totally stolen from project \"[jscodeshift](https://github.com/facebook/jscodeshift)\" ;-)\n\n\n\nTable of contents\n=================\n\n- [Requirements](#requirements)\n- [Features](#features)\n- [Install \u0026 Run](#install--run)\n- [CLI usage](#cli-usage)\n- [Writing a codemod](#writing-a-codemod)\n    - [Codemod file](#codemod-file)\n    - [Ways to transform](#ways-to-transform)\n    - [Traversal transformation](#traversal-transformation)\n    - [Manual transformation](#manual-transformation)\n- [Programmable API](#programmable-api)\n    - [CodemodRunner](#codemodrunner)\n    - [CodeTransformer](#codetransformer)\n    - [AbstractTracer: Custom logging](#abstracttracer-custom-logging)\n\n\n\nRequirements\n============\n\nDue to dependencies to [PHP-Parser](https://github.com/nikic/PHP-Parser) 4.x, following PHP versions are required:\n\n* Running codeshift: PHP 7.0 or higher\n* Target code to be transformed: PHP 5.2 or higher\n\n\n\nFeatures\n========\n\n* Simple CLI for:\n    * Dumping the AST of a file to stout or an output file\n    * Transforming a single source file using a codemod\n    * Transforming sources in a directory tree using a codemod\n* Clear API for writing a codemod\n* Clear API for executing codemods programmatically\n\n\n\nInstall \u0026 Run\n=============\n\nThe easiest way to install Codeshift is to add it to your project using [Composer](https://getcomposer.org).\n\n1. Require the library as a dependency using Composer:\n\n    ```text\n    php composer.phar require atanamo/php-codeshift\n    ```\n\n2. Install the library:\n\n    ```text\n    php composer.phar install\n    ```\n\n3. Execute the Codeshift CLI (Print help):\n\n    ```text\n    vendor/bin/codeshift --help\n    ```\n\n\n\nCLI usage\n=========\n\nTo execute a codemod file on a source directory:\n```text\nvendor/bin/codeshift --mod=/my/codemod.php --src=/my/project/src\n```\n\nUse `--out` to not change original source:\n```text\nvendor/bin/codeshift --mod=/my/codemod.php --src=/my/project/src --out=/transformed/src\n```\n\nDump AST to file:\n```text\nvendor/bin/codeshift --ast=/my/script.php --out=/my/script_ast.txt\n```\n\nNote:\nIf you are on windows, you may need to use `call` to access the binary:\n```text\ncall vendor/bin/codeshift ...\n```\n\n\n\nWriting a codemod\n=================\n\nCodemod file\n------------\n\nA codemod is a PHP file that defines the transformations to do on your PHP source code / source file.\n\nThe only thing needed in the file is to export a class derived from `Codeshift\\AbstractCodemod`.\nThis class can define/override one or more of the provided hook methods:\n\n```php\n\u003c?php\n\nuse Codeshift\\AbstractCodemod;\n\nclass YourAwesomeCodemod extends AbstractCodemod {\n\n    public function init() {\n        // Do some own initializations and environment setup here\n    }\n\n    public function beforeTraversalTransform(array $statements): array {\n        // Do some manual transformations here,\n        // they are applied before starting the main code traversal\n\n        return $statements;\n    }\n\n    public function afterTraversalTransform(array $statements): array {\n        // Do some manual transformations here,\n        // they are applied after the main code traversal was done\n\n        return $statements;\n    }\n}\n\nreturn 'YourAwesomeCodemod';  // Export name of the codemod class\n```\n\nPlease see source file [`\u003ccodeshift\u003e/src/AbstractCodemod.php`](./src/AbstractCodemod.php) for detail documentation of the `AbstractCodemod` class.\n\nAlso see [`\u003ccodeshift\u003e/samples/foobar_codemod.php`](./samples/foobar_codemod.php) for an example codemod.\n\nIf you declare the codemod class in an own namespace, do not forget to export/return it correctly:\n```php\nreturn '\\YourNamespace\\YourAwesomeCodemod';\n```\n\n\nWays to transform\n-----------------\n\nBasically, you can differ two ways of how to transform your code:\n\n1. Traversal transformation using so-called \"Node visitors\"\n2. Manual transformation using hard-core API\n\nFor **traversal transformation** you almost always only need the `init` hook of a codemod.\nThe `before` and `after` hooks are intended for the more specific tasks of a **manual transformation**.\nBut of course you can mix them as you need.\n\nFor the transformations themselves you will have to rely heavily on the API of [PHP-Parser](https://github.com/nikic/PHP-Parser).\n\n\nTraversal transformation\n------------------------\n\nTraversal transformation means to transform the AST while traversing it.\nThe transformation itself is defined by one or more \"node visitors\".\nThe visitors are applied to each node of the AST.\n\nPlease see the excellent documentation of PHP-Parser for this:\n[PHP-Parser/doc/component/Walking_the_AST](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown)\n\nWhile PHP-Parser refers to the class `PhpParser\\NodeTraverser` for applying a visitor,\nPHP-Codeshift provides some convenient mechanism for this.\n\nNormally you only have to add your visitor in the `init` hook of your codemod:\n\n```php\n    public function init() {\n        // Init the your visitor\n        $visitor = new YourAwesomeVisitor();\n\n        // Schedule a traversal run on the code that uses the visitor\n        $this-\u003eaddTraversalTransform($visitor);\n    }\n```\n\nEach call to `addTraversalTransform()` will create one `PhpParser\\NodeTraverser` internally, which is then used to do one traversal run on the AST.\n\nIf you want to use multiple visitors on one single run, just pass them all:\n\n```php\n$this-\u003eaddTraversalTransform($visitor1, $visitor2, $visitor3);\n```\n\nYou may define the visitor classes in the codemod file or require them from other files.\n\nNote that each of your visitors must implement the interface `PhpParser\\NodeVisitor`. Therefor it's recommended to simply extend the abstract class `PhpParser\\NodeVisitorAbstract`.\nDo not forget to use the correct namespace.\n\n\nManual transformation\n---------------------\n\nManual transformation includes everything that differs from traversal transformation.\n\nYou can use the hook methods `beforeTraversalTransform` and/or `afterTraversalTransform` to manipulate the passed statement nodes as you like.\n\nIn that case, the AST as a whole is represented by the statement nodes.\nTherefor, to simplify finding specific nodes of the AST, the PHP-Parser library provides the `PhpParser\\NodeFinder` class.\n\nThe documentation of PHP-Parser contains some examples of how to use the `NodeFinder`:\n[PHP-Parser/doc/component/Walking_the_AST#simple-node-finding](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown#simple-node-finding)\n\nThe following is an example in the context of a codemod.\nIt simply searches for the first function definition in the AST and renames the found function to \"foobar\":\n\n```php\n\u003c?php\n\nuse Codeshift\\AbstractCodemod;\nuse PhpParser\\{Node, NodeFinder};\n\nclass YourManualCodemod extends AbstractCodemod {\n\n    public function beforeTraversalTransform(array $statements): array {\n        $nodeFinder = new NodeFinder();\n        $functionNode = $nodeFinder-\u003efindFirstInstanceOf($statements, Node\\Stmt\\Function_::class);\n\n        if ($functionNode != null) {\n            $functionNode-\u003ename = new Node\\Identifier('foobar');\n        }\n\n        return $statements;\n    }\n}\n\nreturn 'YourManualCodemod';\n```\n\n\n\nProgrammable API\n================\n\nWhile in most cases it will be sufficient to use the CLI, the library offers a few useful classes for manual execution of codemods.\n\nMainly these are:\n* [`CodemodRunner`](./src/CodemodRunner.php)\n* [`CodeTransformer`](./src/CodemodRunner.php)\n* [`AbstractTracer`](./src/AbstractTracer.php)\n\n\nCodemodRunner\n-------------\n\nThe `CodemodRunner` is the most convienent way to execute one or more codemods by using their file names.\n\nThe following example shows how to use the `CodemodRunner` for executing 4 different codemods sequentially by an automated routine:\n\n```php\n\u003c?php\n\n$codemodPaths = [\n    'path/to/my/codemod_1.php',\n    'path/to/my/codemod_2.php',\n    'path/to/my/codemod_3.php',\n    'path/to/my/codemod_4.php'\n];\n\n$srcPath = 'my/project/src';\n$outPath = 'my/project/transformed_src';\n$ignorePaths = [\n    'some/special/file_to_skip.php'\n    'some/very/special/dir_to_skip'\n];\n\n// Execute the codemods\ntry {\n    $runner = new \\Codeshift\\CodemodRunner();\n    $runner-\u003eaddCodemods($codemodPaths);\n    $runner-\u003eexecute($srcPath, $outPath, $ignorePaths);\n}\ncatch (\\Exception $ex) {\n    echo \"Error while executing codemods!\\n\";\n    echo $ex-\u003egetMessage();\n}\n```\n\nPlease see the class documentation for further methods and details.\n\n\nCodeTransformer\n---------------\n\nThe `CodeTransformer` provides some more basic routines for executing a codemod.\nIt is used by the `CodemodRunner` internally.\n\nThe following example uses the `CodeTransformer` to transform code from a string that is fetched by a custom function.\n```php\n\u003c?php\n\n$codemod = new MySpecialCodemod();\n\n$transformer = new \\Codeshift\\CodeTransformer();\n$transformer-\u003esetCodemod($codemod);\n\n// Store some PHP code to string\n$codeString = getSomeCodeInput();\n\n// Execute the codemod\ntry {\n    echo $transformer-\u003erunOnCode($codeString);\n}\ncatch (\\Exception $ex) {\n    echo \"Error while executing code transformation!\\n\";\n    echo $ex-\u003egetMessage();\n}\n```\n\nPlease see the class documentation for further methods and details.\n\n\nAbstractTracer: Custom logging\n------------------------------\n\nIf you use the API classes, they generate some logs to STDOUT by default.\nFor instance, the execution of a codemod is logged and each transformation of a file.\n\nThese logs are generated by the `SimpleOutputTracer`, which is the default implementation for the `AbstractTracer` class.\n\nYou can implement your own \"Tracer\" by deriving the `AbstractTracer` and providing it to the `CodemodRunner` or `CodeTransformer`.\n\nThis allows you to log processing steps to e.g. a special stream. You may also implement a kind of adapter by this, which provides information for continuous-integration frameworks or similar.\n\n\nThe following example defines an own \"Tracer\" and passes it to the `CodemodRunner`:\n```php\n\u003c?php\n\nclass MySpecialTracer extends AbstractTracer {\n    public function writeLine($text='') {\n        echo '\u003cp\u003e', $text, '\u003c/p\u003e';\n    }\n}\n\n$runner = new \\Codeshift\\CodemodRunner('path/to/my/codemod.php', new MySpecialTracer());\n$runner-\u003eexecuteSecured('my/project/src');\n```\n\nThe custom tracer `MySpecialTracer` simply writes all output as HTML paragraphs.\nAlso note that the example uses `executeSecured` here, to even log exceptions by the custom tracer.\n\nFrom a practical point of view, it might be more useful to override other methods with custom implementations.\nHere are a few examples:\n\n* `traceFileTransformation($inputFilePath, $outputFilePath, $fileChanged)`\n* `traceCodemodLoaded($codemod, $codemodPath)`\n* `traceException($exception, $withStackTrace)`\n\nPlease see the class documentation for further methods and details.\n\n\n","funding_links":[],"categories":["PHP","By Environment","Misc"],"sub_categories":["PHP","ant-design"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAtanamo%2FPHP-Codeshift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAtanamo%2FPHP-Codeshift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAtanamo%2FPHP-Codeshift/lists"}