add Mysql to Mysqli set

This commit is contained in:
Tomas Votruba 2018-12-13 04:12:09 +01:00
parent a43986832f
commit d376e90319
13 changed files with 461 additions and 3 deletions

View File

@ -0,0 +1,36 @@
# https://stackoverflow.com/a/1390625/1348344
# https://github.com/philip/MySQLConverterTool/blob/master/Converter.php
services:
Rector\Rector\Function_\FunctionReplaceRector:
mysql_affected_rows: 'mysqli_affected_rows'
mysql_close: 'mysqli_close'
mysql_data_seek: 'mysqli_data_seek'
mysql_errno: 'mysqli_errno'
mysql_error: 'mysqli_error'
mysql_fetch_array: 'mysqli_fetch_array'
mysql_fetch_assoc: 'mysqli_fetch_assoc'
mysql_fetch_lengths: 'mysqli_fetch_lengths'
mysql_fetch_object: 'mysqli_fetch_object'
mysql_fetch_row: 'mysqli_fetch_row'
mysql_field_seek: 'mysqli_field_seek'
mysql_free_result: 'mysqli_free_result'
mysql_get_client_info: 'mysqli_get_client_info'
mysql_get_host_info: 'mysqli_get_host_info'
mysql_get_proto_info: 'mysqli_get_proto_info'
mysql_get_server_info: 'mysqli_get_server_info'
mysql_info: 'mysqli_info'
mysql_insert_id: 'mysqli_insert_id'
mysql_num_rows: 'mysqli_num_rows'
mysql_ping: 'mysqli_ping'
mysql_query: 'mysqli_query'
mysql_real_escape_string: 'mysqli_real_escape_string'
mysql_select_db: 'mysqli_select_db'
mysql_set_charset: 'mysqli_set_charset'
mysql_stat: 'mysqli_stat'
mysql_thread_id: 'mysqli_thread_id'
mysql_numfields: 'mysqli_num_fields'
mysql_escape_string: 'mysqli_real_escape_string'
mysql_client_encoding: 'mysqli_character_set_name'
mysql_numrows: 'mysqli_num_rows'
mysql_list_processes: 'mysqli_thread_id'
mysql_num_fields: 'mysqli_field_count'

View File

@ -1,3 +1,6 @@
imports:
- { resource: "mysql_to_mysqli.yml" }
services:
Rector\Php\Rector\FunctionLike\Php4ConstructorRector: ~
Rector\Php\Rector\Ternary\TernaryToNullCoalescingRector: ~

View File

@ -0,0 +1,175 @@
<?php declare(strict_types=1);
namespace Rector\Php\Rector\Assign;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\BinaryOp\Smaller;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\PostInc;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\For_;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://www.phpclasses.org/blog/package/9199/post/3-Smoothly-Migrate-your-PHP-Code-using-the-Old-MySQL-extension-to-MySQLi.html
*/
final class MysqlAssignToMysqliRector extends AbstractRector
{
/**
* @var string[]
*/
private $fieldToFieldDirect = [
'mysql_field_len' => 'length',
'mysql_field_name' => 'name',
'mysql_field_table' => 'table',
];
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Converts more complex mysql functions to mysqli',
[
new CodeSample(
<<<'CODE_SAMPLE'
$data = mysql_db_name($result, $row);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
mysqli_data_seek($result, $row);
$fetch = mysql_fetch_row($result);
$data = $fetch[0];
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
if (! $node->expr instanceof FuncCall) {
return null;
}
/** @var FuncCall $funcCallNode */
$funcCallNode = $node->expr;
if ($this->isName($funcCallNode, 'mysql_tablename')) {
return $this->processMysqlTableName($node, $funcCallNode);
}
if ($this->isName($funcCallNode, 'mysql_db_name')) {
return $this->processMysqlDbName($node, $funcCallNode);
}
if ($this->isName($funcCallNode, 'mysql_db_query')) {
return $this->processMysqliSelectDb($node, $funcCallNode);
}
if ($this->isName($funcCallNode, 'mysql_fetch_field')) {
return $this->processMysqlFetchField($node, $funcCallNode);
}
return $this->processFieldToFieldDirect($node, $funcCallNode);
}
private function processMysqlDbName(Assign $assignNode, FuncCall $funcCallNode): FuncCall
{
$funcCallNode->name = new Name('mysqli_data_seek');
$mysqlFetchRowFuncCall = new FuncCall(new Name('mysql_fetch_row'), [$funcCallNode->args[0]]);
$fetchVariable = new Variable('fetch');
$newAssignNode = new Assign($fetchVariable, $mysqlFetchRowFuncCall);
$this->addNodeAfterNode($newAssignNode, $assignNode);
$newAssignNode = new Assign($assignNode->var, new ArrayDimFetch($fetchVariable, new LNumber(0)));
$this->addNodeAfterNode($newAssignNode, $assignNode);
return $funcCallNode;
}
private function processMysqliSelectDb(Assign $assignNode, FuncCall $funcCallNode): FuncCall
{
$funcCallNode->name = new Name('mysqli_select_db');
$newAssignNode = new Assign($assignNode->var, new FuncCall(new Name('mysqli_query'), [$funcCallNode->args[1]]));
$this->addNodeAfterNode($newAssignNode, $assignNode);
unset($funcCallNode->args[1]);
return $funcCallNode;
}
private function processMysqlFetchField(Assign $assignNode, FuncCall $funcCallNode): Assign
{
$funcCallNode->name = new Name('mysqli_fetch_field');
if (! isset($funcCallNode->args[1])) {
return $assignNode;
}
unset($funcCallNode->args[1]);
// add for
$xVar = new Variable('x');
$forNode = new For_([
'init' => [new Assign($xVar, new LNumber(0))],
'cond' => [new Smaller($xVar, new LNumber(5))],
'loop' => [new PostInc($xVar)],
'stmts' => [new Expression($funcCallNode)],
]);
$this->addNodeAfterNode($forNode, $assignNode->getAttribute(Attribute::PREVIOUS_EXPRESSION));
return $assignNode;
}
private function processMysqlTableName(Assign $assignNode, FuncCall $funcCall): FuncCall
{
$funcCall->name = new Name('mysqli_data_seek');
$newFuncCall = new FuncCall(new Name('mysql_fetch_array'), [$funcCall->args[0]]);
$newAssignNode = new Assign($assignNode->var, new ArrayDimFetch($newFuncCall, new LNumber(0)));
$this->addNodeAfterNode($newAssignNode, $assignNode);
return $funcCall;
}
private function processFieldToFieldDirect(Assign $assignNode, FuncCall $funcCallNode): ?Assign
{
foreach ($this->fieldToFieldDirect as $funcName => $property) {
if ($this->isName($funcCallNode, $funcName)) {
if ($funcCallNode->getAttribute(Attribute::PARENT_NODE) instanceof PropertyFetch) {
continue;
}
$funcCallNode->name = new Name('mysqli_fetch_field_direct');
$assignNode->expr = new PropertyFetch($funcCallNode, $property);
return $assignNode;
}
}
return null;
}
}

View File

@ -0,0 +1,92 @@
<?php declare(strict_types=1);
namespace Rector\Php\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://www.phpclasses.org/blog/package/9199/post/3-Smoothly-Migrate-your-PHP-Code-using-the-Old-MySQL-extension-to-MySQLi.html
*/
final class MysqlFuncCallToMysqliRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Converts more complex mysql functions to mysqli',
[
new CodeSample(
<<<'CODE_SAMPLE'
mysql_drop_db($database);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
mysqli_query('DROP DATABASE ' . $database);
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if ($this->isName($node, 'mysql_drop_db')) {
return $this->processMysqlDropDb($node);
}
if ($this->isName($node, 'mysql_list_dbs')) {
$node->name = new Name('mysqli_query');
$node->args[0] = new Arg(new String_('SHOW DATABASES'));
}
if ($this->isName($node, 'mysql_list_fields')) {
$node->name = new Name('mysqli_query');
$node->args[0]->value = $this->joinStringWithNode('SHOW COLUMNS FROM', $node->args[1]->value);
unset($node->args[1]);
}
if ($this->isName($node, 'mysql_list_tables')) {
$node->name = new Name('mysqli_query');
$node->args[0]->value = $this->joinStringWithNode('SHOW TABLES FROM', $node->args[0]->value);
}
return $node;
}
private function processMysqlDropDb(FuncCall $funcCallNode): FuncCall
{
$funcCallNode->name = new Name('mysqli_query');
$funcCallNode->args[0]->value = $this->joinStringWithNode('DROP DATABASE', $funcCallNode->args[0]->value);
return $funcCallNode;
}
private function joinStringWithNode(string $string, Expr $node): Expr
{
if ($node instanceof String_) {
return new String_($string . ' ' . $node->value);
}
return new Concat(new String_($string . ' '), $node);
}
}

View File

@ -0,0 +1,26 @@
<?php
function mysql_to_mysqli_assign() {
$result = [];
$row = 1;
$data = mysql_db_name($result, $row);
$result = mysql_db_query('database', 'query');
}
?>
-----
<?php
function mysql_to_mysqli_assign() {
$result = [];
$row = 1;
mysqli_data_seek($result, $row);
$fetch = mysql_fetch_row($result);
$data = $fetch[0];
mysqli_select_db('database');
$result = mysqli_query('query');
}
?>

View File

@ -0,0 +1,32 @@
<?php
function mysql_to_mysqli_assign2() {
$result = [];
$fetch = mysql_fetch_field($result, 5);
$fetch = mysql_fetch_field($result);
$length = mysql_field_len($result, 5);
$name = mysql_field_name($result, 5);
$table = mysql_field_table($result, 5);
}
?>
-----
<?php
function mysql_to_mysqli_assign2() {
$result = [];
for ($x = 0; $x < 5; $x++) {
mysqli_fetch_field($result);
}
$fetch = mysqli_fetch_field($result);
$fetch = mysqli_fetch_field($result);
$length = mysqli_fetch_field_direct($result, 5)->length;
$name = mysqli_fetch_field_direct($result, 5)->name;
$table = mysqli_fetch_field_direct($result, 5)->table;
}
?>

View File

@ -0,0 +1,18 @@
<?php
function mysql_to_mysqli_assign3() {
$result = [];
$name = mysql_tablename($result, 3);
}
?>
-----
<?php
function mysql_to_mysqli_assign3() {
$result = [];
mysqli_data_seek($result, 3);
$name = mysql_fetch_array($result)[0];
}
?>

View File

@ -0,0 +1,23 @@
<?php declare(strict_types=1);
namespace Rector\Php\Tests\Rector\Assign\MysqlAssignToMysqliRector;
use Rector\Php\Rector\Assign\MysqlAssignToMysqliRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class MysqlAssignToMysqliRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([
__DIR__ . '/Fixture/fixture.php.inc',
__DIR__ . '/Fixture/fixture2.php.inc',
__DIR__ . '/Fixture/fixture3.php.inc',
]);
}
public function getRectorClass(): string
{
return MysqlAssignToMysqliRector::class;
}
}

View File

@ -0,0 +1,31 @@
<?php
function mysql_to_mysqli_func_call() {
mysql_drop_db('database_name');
$database = 'database_name';
mysql_drop_db($database);
$list = mysql_list_dbs();
$list = mysql_list_tables($database);
mysql_list_fields($database_name, $table_name);
}
?>
-----
<?php
function mysql_to_mysqli_func_call() {
mysqli_query('DROP DATABASE database_name');
$database = 'database_name';
mysqli_query('DROP DATABASE ' . $database);
$list = mysqli_query('SHOW DATABASES');
$list = mysqli_query('SHOW TABLES FROM ' . $database);
mysqli_query('SHOW COLUMNS FROM ' . $table_name);
}
?>

View File

@ -0,0 +1,19 @@
<?php declare(strict_types=1);
namespace Rector\Php\Tests\Rector\FuncCall\MysqlFuncCallToMysqliRector;
use Rector\Php\Rector\FuncCall\MysqlFuncCallToMysqliRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class MysqlFuncCallToMysqliRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([__DIR__ . '/Fixture/fixture.php.inc']);
}
public function getRectorClass(): string
{
return MysqlFuncCallToMysqliRector::class;
}
}

View File

@ -30,6 +30,9 @@ parameters:
- '#(.*?)\(\) should return Psr\\Container\\ContainerInterface but returns Symfony\\Component\\DependencyInjection\\ContainerInterface\|null#'
# false positives
- '#Instanceof between PhpParser\\Node\\Expr\\FuncCall and PhpParser\\Node\\Expr\\Assign will always evaluate to false#'
- '#Result of && is always false#'
- '#Offset string does not exist on string#' # 1
- '#Array \(array<array<PhpParser\\Node\\Stmt>>\) does not accept array<PhpParser\\Node\\Stmt|null>#'
- '#Property Rector\\DependencyInjection\\Loader\\RectorServiceParametersShifter::\$serviceKeywords \(array<string>\) does not accept ReflectionProperty#'
@ -46,6 +49,7 @@ parameters:
- '#Method Rector\\NodeTypeResolver\\PhpDoc\\NodeAnalyzer\\DocBlockAnalyzer::getTagByName\(\) should return PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode but returns PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode\|null#'
- '#Parameter \#1 \$expr of class PhpParser\\Node\\Expr\\BooleanNot constructor expects PhpParser\\Node\\Expr, PhpParser\\Node given#'
- '#Parameter \#1 \$binaryOpNode of method Rector\\CodeQuality\\Rector\\Identical\\SimplifyConditionsRector::createInversedBooleanOp\(\) expects PhpParser\\Node\\Expr\\BinaryOp, PhpParser\\Node given#'
- '#Parameter \#1 \$node of method Rector\\PhpParser\\Node\\Commander\\NodeAddingCommander::wrapToExpression\(\) expects PhpParser\\Node\\Expr\|PhpParser\\Node\\Stmt, PhpParser\\Node given#'
# irelevant
- '#Call to function in_array\(\) with arguments string, (.*?) and true will always evaluate to false#'

View File

@ -41,7 +41,7 @@ final class NodeAddingCommander implements CommanderInterface
$this->betterNodeFinder = $betterNodeFinder;
}
public function addNodeAfterNode(Expr $node, Node $positionNode): void
public function addNodeAfterNode(Node $node, Node $positionNode): void
{
$position = $this->resolveNearestExpressionPosition($positionNode);

View File

@ -3,7 +3,6 @@
namespace Rector\Rector;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Class_;
use Rector\Application\AppliedRectorCollector;
use Rector\PhpParser\Node\Commander\NodeAddingCommander;
@ -47,7 +46,7 @@ trait NodeCommandersTrait
$this->propertyAddingCommander = $propertyAddingCommander;
}
protected function addNodeAfterNode(Expr $node, Node $positionNode): void
protected function addNodeAfterNode(Node $node, Node $positionNode): void
{
$this->nodeAddingCommander->addNodeAfterNode($node, $positionNode);