PhpSqlReplacer::replaceValueFromFile()   A
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 18
c 0
b 0
f 0
rs 9.2222
cc 6
nc 4
nop 4
1
<?php
2
3
namespace PhpSqlReplacer;
4
5
use PhpSqlReplacer\Exceptions\SqlReplacerException;
6
use PhpSqlReplacer\External\Serialization;
7
8
/**
9
 * PhpSqlReplacer - Main Driver Class
10
 * @author gggordon
11
 * @copyright gggordon 2020
12
 * @license MIT
13
 */
14
class PhpSqlReplacer
15
{
16
    /**
17
     * Extracts full record values and column names from insert statements within a file
18
     * For eg. if the the record contains `'I like trading'` and `trading` is the pattern being matched
19
     * `'I like trading'` will be extracted.
20
     *
21
     * @param string $filePath - Path to source file
22
     * @param string $patternToMatchFor - Optional pattern to match for. Default: null
23
     * @throws Exceptions\SqlReplacerException
24
     * @param bool  $includeColumnNames - Whether to include column names. Default:false
25
     * @return string[]
26
     */
27
    public function extractSqlValuesFromFile($filePath, $patternToMatchFor=null, $includeColumnNames=false)
28
    {
29
        if (!file_exists($filePath) || !is_string($filePath)) {
30
            throw new SqlReplacerException("File '$filePath' does not exist");
31
        }
32
        return $this->extractSqlValues(
33
           file_get_contents($filePath),
34
           $patternToMatchFor,
35
           $includeColumnNames
36
       );
37
    }
38
39
    /**
40
     * Extracts full record values and column names from insert statements within a string
41
     * For eg. if the the record contains `'I like trading'` and `trading` is the pattern being matched
42
     * `'I like trading'` will be extracted.
43
     *
44
     * @param string $contents - SQL Contents to search
45
     * @param string $patternToMatchFor - Optional pattern to match for. Default: null
46
     * @throws Exceptions\SqlReplacerException
47
     * @param bool  $includeColumnNames - Whether to include column names. Default:false
48
     * @return string[]
49
     */
50
    public function extractSqlValues($contents, $patternToMatchFor=null, $includeColumnNames=false)
51
    {
52
        $tokens=[];
53
        if (preg_match_all("/\(.*\)/U", $contents, $matches)) {
54
            $tokens=$matches[0];
55
        }
56
        
57
        $tokens = array_filter($tokens, function ($token) {
58
            return $token[0]=="(" && $token[strlen($token)-1]==")";
59
        });
60
        $tokens = array_map(function ($piece) use ($includeColumnNames) {
61
            $piece = trim(trim($piece, "("), ")");
62
            $pieces = explode(",", $piece);
63
            $pieces= array_filter(array_map('trim', $pieces), function ($p) {
64
                return !empty($p) && strlen($p)>0;
65
            });
66
            $pieces = array_values($pieces);
67
            if (!$includeColumnNames && count($pieces)>0) {
68
                $firstPiece = $pieces[0];
69
                
70
                
71
                if ($firstPiece[0] != "'" &&
72
                    $firstPiece[0] != "\"" &&
73
                    preg_match("/[a-zA-Z]/", $firstPiece[0])
74
                    ) {
75
                    return [];
76
                }
77
            }
78
            return $pieces;
79
        }, $tokens);
80
        
81
        $matches = [];
82
        array_walk_recursive($tokens, function ($value) use (&$matches,$patternToMatchFor) {
83
            if ($patternToMatchFor===null ||
84
                       stripos($value, $patternToMatchFor) !== false) {
85
                $matches[]=$value;
86
            }
87
        });
88
        
89
        
90
        return $matches;
91
    }
92
93
    /**
94
     * Safely (including managing serialized data) replaces matched string patterns in a string
95
     *
96
     * @param string $contents - String to search
97
     * @param string $value - Value to replace
98
     * @param string $replaceWith - Value to replace with
99
     * @return string Updated Contents
100
     */
101
    public function replaceValue($contents, $value, $replaceWith)
102
    {
103
        set_error_handler(function ($errno, $errstr, $errfile, $errline) {
104
            throw new \ErrorException($errstr, $errno, 0, $errfile, $errline);
105
        });
106
        $matches = $this->extractSqlValues($contents, $value);
107
        
108
        foreach ($matches as $match) {
109
            $matchWithoutQuotes = str_replace("\\\"", "\"", trim(trim($match, "'")));
110
            if (Serialization::isSerializedValue($matchWithoutQuotes)) {
111
                $matchReplaced = str_replace($value, $replaceWith, $matchWithoutQuotes);
112
                $matchReplaced = preg_replace_callback('!s:(\d+):"(.*?)";!', function ($match) {
113
                    return ($match[1] == strlen($match[2])) ? $match[0] : 's:' . strlen($match[2]) . ':"' . $match[2] . '";';
114
                }, $matchReplaced);
115
                try {
116
                    unserialize($matchReplaced);
117
                } catch (\Exception $e) {
118
                    //silently ignore
119
                    continue;
120
                }
121
                $matchReplaced = "'$matchReplaced'";
122
            } else {
123
                $matchReplaced = str_replace($value, $replaceWith, $match);
124
            }
125
            $contents = str_replace($match, $matchReplaced, $contents);
126
        }
127
        $contents=str_replace($value, $replaceWith, $contents);
128
        restore_error_handler();
129
        return $contents;
130
    }
131
132
    /**
133
     * Safely (including managing serialized data) replaces matched string patterns in a file and optionally saves to another file
134
     *
135
     * @param string $sourceFile - Source file to search
136
     * @param string $value - Value to replace
137
     * @param string $replaceWith - Value to replace with
138
     * @param string $destinationFile - Destination file to write to
139
     * @throws Exceptions\SqlReplacerException
140
     * @return string Updated Contents
141
     */
142
    public function replaceValueFromFile($sourceFile, $value, $replaceWith, $destinationFile=null)
143
    {
144
        if (!file_exists($sourceFile) || !is_string($sourceFile)) {
145
            throw new SqlReplacerException("Source File '$sourceFile' does not exist");
146
        }
147
        if (is_null($destinationFile) || !is_string($sourceFile)) {
148
            throw new SqlReplacerException("Destination File '$destinationFile'  is required");
149
        }
150
        $contents = $this->replaceValue(
151
             file_get_contents($sourceFile),
152
             $value,
153
             $replaceWith
154
        );
155
        
156
        if ($destinationFile) {
157
            file_put_contents($destinationFile, $contents);
158
        }
159
        return $contents;
160
    }
161
162
163
}
164