feat: Add initial structure for /core:refactor command (#91)
This commit introduces the initial framework for the `/core:refactor` command. Summary of work: - Created the command definition in `claude/code/commands/refactor.md`. - Implemented a PHP script, `claude/code/scripts/refactor.php`, to handle the refactoring logic. - Set up a PHP environment with `composer` and added the `nikic/php-parser` dependency for AST manipulation. - Implemented a proof-of-concept for the `extract-method` subcommand. Challenges and Implementation Details: The initial implementation attempt using shell scripting (`sed`, `awk`, `perl`) proved to be unreliable for source code manipulation, resulting in corrupted files. This approach was abandoned in favor of a more robust solution using a proper PHP parser. The current implementation uses the `nikic/php-parser` library to traverse the Abstract Syntax Tree (AST) of a PHP file. A `MethodExtractor` visitor identifies a hardcoded selection of code within a test file (`Test.php`), extracts the relevant AST nodes into a new method, and replaces the original nodes with a call to the new method. This is a non-functional proof-of-concept and requires further development to become a dynamic, user-driven tool. The file path, selection, and new method name are currently hardcoded for demonstration purposes.
This commit is contained in:
parent
dee598e39f
commit
5d62464627
6 changed files with 237 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
.idea/
|
||||
vendor/
|
||||
claude/api/php/vendor/
|
||||
__pycache__/
|
||||
.env
|
||||
|
|
|
|||
20
Test.php
Normal file
20
Test.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
class Test
|
||||
{
|
||||
public function originalMethod()
|
||||
{
|
||||
// Some code before the selection
|
||||
echo "Before";
|
||||
|
||||
// Start of selection
|
||||
$a = 1;
|
||||
$b = 2;
|
||||
$c = $a + $b;
|
||||
echo $c;
|
||||
// End of selection
|
||||
|
||||
// Some code after the selection
|
||||
echo "After";
|
||||
}
|
||||
}
|
||||
33
claude/code/commands/refactor.md
Normal file
33
claude/code/commands/refactor.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: refactor
|
||||
description: Guided refactoring with safety checks
|
||||
args: <subcommand> [args]
|
||||
---
|
||||
|
||||
# Refactor
|
||||
|
||||
Guided refactoring with safety checks.
|
||||
|
||||
## Subcommands
|
||||
|
||||
- `extract-method <new-method-name>` - Extract selection to a new method
|
||||
- `rename <new-name>` - Rename a class, method, or variable
|
||||
- `move <new-namespace>` - Move a class to a new namespace
|
||||
- `inline` - Inline a method
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/core:refactor extract-method validateToken
|
||||
/core:refactor rename User UserV2
|
||||
/core:refactor move App\\Models\\User App\\Data\\Models\\User
|
||||
/core:refactor inline calculateTotal
|
||||
```
|
||||
|
||||
## Action
|
||||
|
||||
This command will run the refactoring script:
|
||||
|
||||
```bash
|
||||
~/.claude/plugins/code/scripts/refactor.php "<subcommand>" [args]
|
||||
```
|
||||
108
claude/code/scripts/refactor.php
Normal file
108
claude/code/scripts/refactor.php
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../../../vendor/autoload.php';
|
||||
|
||||
use PhpParser\ParserFactory;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\PrettyPrinter;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
class MethodExtractor extends NodeVisitorAbstract
|
||||
{
|
||||
private $startLine;
|
||||
private $endLine;
|
||||
private $newMethodName;
|
||||
|
||||
public function __construct($startLine, $endLine, $newMethodName)
|
||||
{
|
||||
$this->startLine = $startLine;
|
||||
$this->endLine = $endLine;
|
||||
$this->newMethodName = $newMethodName;
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
$classNode = $node;
|
||||
$originalMethod = null;
|
||||
$extractionStartIndex = -1;
|
||||
$extractionEndIndex = -1;
|
||||
|
||||
foreach ($classNode->stmts as $stmt) {
|
||||
if ($stmt instanceof ClassMethod) {
|
||||
foreach ($stmt->stmts as $index => $mstmt) {
|
||||
if ($mstmt->getStartLine() >= $this->startLine && $extractionStartIndex === -1) {
|
||||
$extractionStartIndex = $index;
|
||||
}
|
||||
if ($mstmt->getEndLine() <= $this->endLine && $extractionStartIndex !== -1) {
|
||||
$extractionEndIndex = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if ($extractionStartIndex !== -1) {
|
||||
$originalMethod = $stmt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($originalMethod !== null) {
|
||||
$statementsToExtract = array_slice(
|
||||
$originalMethod->stmts,
|
||||
$extractionStartIndex,
|
||||
$extractionEndIndex - $extractionStartIndex + 1
|
||||
);
|
||||
|
||||
$newMethod = new ClassMethod($this->newMethodName, [
|
||||
'stmts' => $statementsToExtract
|
||||
]);
|
||||
$classNode->stmts[] = $newMethod;
|
||||
|
||||
$methodCall = new Node\Expr\MethodCall(new Node\Expr\Variable('this'), $this->newMethodName);
|
||||
$methodCallStatement = new Node\Stmt\Expression($methodCall);
|
||||
|
||||
array_splice(
|
||||
$originalMethod->stmts,
|
||||
$extractionStartIndex,
|
||||
count($statementsToExtract),
|
||||
[$methodCallStatement]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$subcommand = $argv[1] ?? null;
|
||||
|
||||
switch ($subcommand) {
|
||||
case 'extract-method':
|
||||
$filePath = 'Test.php';
|
||||
$startLine = 9;
|
||||
$endLine = 13;
|
||||
$newMethodName = 'newMethod';
|
||||
|
||||
$code = file_get_contents($filePath);
|
||||
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
$ast = $parser->parse($code);
|
||||
|
||||
$traverser = new PhpParser\NodeTraverser();
|
||||
$traverser->addVisitor(new MethodExtractor($startLine, $endLine, $newMethodName));
|
||||
|
||||
$modifiedAst = $traverser->traverse($ast);
|
||||
|
||||
$prettyPrinter = new PrettyPrinter\Standard;
|
||||
$newCode = $prettyPrinter->prettyPrintFile($modifiedAst);
|
||||
|
||||
file_put_contents($filePath, $newCode);
|
||||
|
||||
echo "Refactoring complete.\n";
|
||||
break;
|
||||
default:
|
||||
echo "Unknown subcommand: $subcommand\n";
|
||||
exit(1);
|
||||
}
|
||||
5
composer.json
Normal file
5
composer.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.13"
|
||||
}
|
||||
}
|
||||
70
composer.lock
generated
Normal file
70
composer.lock
generated
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"_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#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "8270d8d541acdfb0be968776d09dbec9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.19.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/51bd93cc741b7fc3d63d20b6bdcd99fdaa359837",
|
||||
"reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-tokenizer": "*",
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"ircmaxell/php-yacc": "^0.0.7",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "lib/PhpParser"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nikita Popov"
|
||||
}
|
||||
],
|
||||
"description": "A PHP parser written in PHP",
|
||||
"keywords": [
|
||||
"parser",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.19.5"
|
||||
},
|
||||
"time": "2025-12-06T11:45:25+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue