Skip to content

Commit 390d4bf

Browse files
committed
initial commit
1 parent 4c110e5 commit 390d4bf

File tree

6 files changed

+225
-1
lines changed

6 files changed

+225
-1
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 phpseclib
3+
Copyright (c) 2025 terrafrost
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# phpseclib3_rector
2+
3+
Rector rules to upgrade a phpseclib v2.0 install to phpseclib v3.0
4+
5+
## Overview
6+
7+
You can use [phpseclib2_compat](https://github.com/phpseclib/phpseclib2_compat) to make all your phpseclib v2.0 calls use phpseclib v3.0, internally, under the hood, or you can use this [Rector](https://getrector.com/) rule to upgrade your phpseclib v2.0 calls to phpseclib v3.0 calls.
8+
9+
## Installation
10+
11+
With [Composer](https://getcomposer.org/):
12+
13+
```
14+
composer require phpseclib/phpseclib2_rector:~1.0
15+
```
16+
## Usage
17+
18+
Create a rector.php file with the following contents:
19+
20+
```php
21+
<?php
22+
use Rector\Config\RectorConfig;
23+
use phpseclib\phpseclib3Rector\Set;
24+
25+
return RectorConfig::configure()
26+
->withSets([Set::PATH]);
27+
```
28+
In the same directory where you created that file you can then run Rector by doing either of these commands:
29+
30+
```
31+
vendor/bin/rector process src --dry-run
32+
vendor/bin/rector process src
33+
```
34+
The files in the `src/` directory will either be full on modified or (in the case of `--dry-run`) the changes that would be made will be previewed.

composer.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "phpseclib/phpseclib3_rector",
3+
"description": "Rector rules for upgrading from phpseclib v2 to phpseclib v3",
4+
"type": "rector-extension",
5+
"require": {
6+
"rector/rector": "^2.0.0"
7+
},
8+
"license": "MIT",
9+
"autoload": {
10+
"psr-4": {
11+
"phpseclib\\phpseclib3Rector\\": "src/"
12+
}
13+
},
14+
"authors": [
15+
{
16+
"name": "terrafrost",
17+
"email": "terrafrost@php.net"
18+
}
19+
]
20+
}

src/RSAMethodRector.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
namespace phpseclib\phpseclib3Rector;
3+
4+
use PhpParser\Node\Expr\Assign;
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PhpParser\Node\Expr\New_;
7+
use PhpParser\Node\Expr\NullsafeMethodCall;
8+
use PhpParser\Node\Expr\StaticCall;
9+
use PhpParser\Node\Name\FullyQualified;
10+
use Rector\Renaming\ValueObject\MethodCallRename;
11+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
12+
13+
/*
14+
This admittedly hackish technique is being employed to facilitate code re-use. RectorPHP defines its
15+
classes as final with private methods so inheritance isn't an option, without this hackish technique
16+
17+
Composition isn't a great option, either, because I'd need to redefine autowire and the other public
18+
methods from AbstractRector. The problem with that is... what if another parameter is added to
19+
autowire? I'd just assume minimize the time I need to spend maintaining the code - not maximize it.
20+
And plus, if a new parameter were added then it seems unlikely that the code would work on older
21+
versions of Rector. Extended AbstractRector isn't an option because enterNode() is declared final in
22+
AbstractRector and implementing RectorInterface is suboptimal because all the method implementations
23+
in AbstractRector would need to be copied and that kinda contradicts the goal is not duplicating code.
24+
*/
25+
$old = file_get_contents(__DIR__ . '\..\..\..\rector\rector\rules\Renaming\Rector\MethodCall\RenameMethodRector.php');
26+
$tokens = \PhpToken::tokenize($old);
27+
$tokens = array_slice($tokens, 1); // remove the <?php
28+
$new = '';
29+
foreach ($tokens as $token) {
30+
switch ("$token") {
31+
case 'final':
32+
continue 2;
33+
case 'RenameMethodRector':
34+
$token = 'NewRenameMethodRector';
35+
break;
36+
case 'private':
37+
$token = 'protected';
38+
}
39+
$new.= $token;
40+
}
41+
eval($new);
42+
43+
class RSAMethodRector extends \Rector\Renaming\Rector\MethodCall\NewRenameMethodRector
44+
{
45+
/**
46+
* @return array<class-string<Node>>
47+
*/
48+
public function getNodeTypes() : array
49+
{
50+
return [MethodCall::class, NullsafeMethodCall::class];
51+
}
52+
53+
/**
54+
* @param \PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\NullsafeMethodCall $call
55+
* @return \PhpParser\Node\Expr\ArrayDimFetch|null|\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\NullsafeMethodCall
56+
*/
57+
protected function refactorMethodCallAndStaticCall($call)
58+
{
59+
$this->methodCallRenames = [
60+
new MethodCallRename('phpseclib\Crypt\RSA', 'loadKey', 'loadKey'),
61+
new MethodCallRename('phpseclib\Crypt\RSA', 'createKey', 'createKey'),
62+
new MethodCallRename('phpseclib\Crypt\RSA', 'setEncryptionMode', 'setEncryptionMode'),
63+
new MethodCallRename('phpseclib\Crypt\RSA', 'setSignatureMode', 'setSignatureMode'),
64+
];
65+
$call = parent::refactorMethodCallAndStaticCall($call);
66+
if (is_null($call)) {
67+
return null;
68+
}
69+
switch ($call->name) {
70+
case 'loadKey':
71+
return new StaticCall(
72+
new FullyQualified('phpseclib3\Crypt\PublicKeyLoader'),
73+
'loadKey',
74+
$call->args
75+
);
76+
case 'createKey':
77+
return new StaticCall(
78+
new FullyQualified('phpseclib3\Crypt\RSA'),
79+
'createKey',
80+
$call->args
81+
);
82+
case 'setEncryptionMode':
83+
case 'setSignatureMode':
84+
$className = $call::CLASS;
85+
$newCall = new $className($call->var, 'withPadding', $call->args);
86+
return new Assign(
87+
$call->var,
88+
$newCall
89+
);
90+
}
91+
}
92+
93+
public function getRuleDefinition() : RuleDefinition
94+
{
95+
return new RuleDefinition('Changes method call names for \phpseclib\Crypt\RSA', []);
96+
}
97+
}

src/RSANewRector.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
namespace phpseclib\phpseclib3Rector;
3+
4+
use PhpParser\Node;
5+
use PhpParser\Node\Expr\Assign;
6+
use PhpParser\Node\Expr\New_;
7+
use PhpParser\Node\Stmt\Expression;
8+
use PhpParser\NodeVisitor;
9+
use PHPStan\Type\ObjectType;
10+
use Rector\Rector\AbstractRector;
11+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
12+
13+
class RSANewRector extends AbstractRector
14+
{
15+
/**
16+
* @return array<class-string<Node>>
17+
*/
18+
public function getNodeTypes(): array
19+
{
20+
return [Expression::class];
21+
}
22+
23+
/**
24+
* @param Expression $node
25+
*/
26+
public function refactor(Node $node)
27+
{
28+
if (! $node->expr instanceof Assign) {
29+
return null;
30+
}
31+
32+
if (! $node->expr->expr instanceof New_) {
33+
return null;
34+
}
35+
36+
if ($this->isObjectType($node->expr->expr->class, new ObjectType('phpseclib\Crypt\RSA'))) {
37+
return NodeVisitor::REMOVE_NODE;
38+
}
39+
40+
return null;
41+
}
42+
43+
public function getRuleDefinition(): RuleDefinition
44+
{
45+
return new RuleDefinition('Delete direct instantiations of \phpseclib\Crypt\RSA', []);
46+
}
47+
}

src/Set.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
namespace phpseclib\phpseclib3Rector;
3+
4+
use Rector\Config\RectorConfig;
5+
use Rector\Renaming\Rector\Name\RenameClassRector;
6+
7+
if (!class_exists('\phpseclib\phpseclib3Rector\Set'))
8+
{
9+
class Set
10+
{
11+
const PATH = __FILE__;
12+
}
13+
}
14+
15+
return RectorConfig::configure()
16+
->withRules([
17+
RSAMethodRector::class,
18+
RSANewRector::class,
19+
])
20+
->withConfiguredRule(RenameClassRector::class, [
21+
'phpseclib\Crypt\RSA' => 'phpseclib3\Crypt\RSA',
22+
'phpseclib\Net\SSH2' => 'phpseclib3\Net\SSH2',
23+
'phpseclib\Net\SFTP' => 'phpseclib3\Net\SFTP',
24+
'phpseclib\Math\BigInteger' => 'phpseclib3\Math\BigInteger',
25+
])
26+
->withImportNames(removeUnusedImports: true);

0 commit comments

Comments
 (0)