Completed
Push — master ( 630231...6cd933 )
by Harry
03:23
created

ReplaceText::getReplacementString()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 19
ccs 12
cts 12
cp 1
rs 8.2222
cc 7
eloc 14
nc 5
nop 2
crap 7
1
<?php
2
/**
3
 * This file is part of graze/data-file
4
 *
5
 * Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/data-file/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/data-file
12
 */
13
14
namespace Graze\DataFile\Modify;
15
16
use Graze\DataFile\Helper\Builder\BuilderAwareInterface;
17
use Graze\DataFile\Helper\GetOptionTrait;
18
use Graze\DataFile\Helper\OptionalLoggerTrait;
19
use Graze\DataFile\Helper\Process\ProcessFactoryAwareInterface;
20
use Graze\DataFile\Node\FileNodeInterface;
21
use Graze\DataFile\Node\LocalFile;
22
use InvalidArgumentException;
23
use Psr\Log\LoggerAwareInterface;
24
use Psr\Log\LogLevel;
25
use Symfony\Component\Process\Exception\ProcessFailedException;
26
27
class ReplaceText implements FileModifierInterface, LoggerAwareInterface, BuilderAwareInterface
28
{
29
    use OptionalLoggerTrait;
30
    use FileProcessTrait;
31
    use GetOptionTrait;
32
33
    /**
34
     * Can this file be modified by this modifier
35
     *
36
     * @param FileNodeInterface $file
37
     *
38
     * @return bool
39
     */
40 1
    public function canModify(FileNodeInterface $file)
41
    {
42
        return (
43 1
            ($file instanceof localFile) &&
44 1
            ($file->exists())
45
        );
46
    }
47
48
    /**
49
     * Modify the file
50
     *
51
     * @param FileNodeInterface $file
52
     * @param array             $options List of options:
53
     *                                   -fromText <string|array> Text to be replace
54
     *                                   -toText <string|array> Text to replace
55
     *                                   -postifx <string> (Default: replace) Set this to blank to replace inline
56
     *                                   -keepOldFile <bool> (Default: true)
57
     *
58
     * @return FileNodeInterface
59
     */
60 5
    public function modify(FileNodeInterface $file, array $options = [])
61
    {
62 5
        $this->options = $options;
63 5
        $fromText = $this->requireOption('fromText');
64 4
        $toText = $this->requireOption('toText');
65
66 3
        unset($options['fromText']);
67 3
        unset($options['toText']);
68
69 3
        if (!($file instanceof LocalFile)) {
70 1
            throw new InvalidArgumentException("Supplied: $file is not a LocalFile");
71
        }
72
73 2
        return $this->replaceText($file, $fromText, $toText, $options);
74
    }
75
76
    /**
77
     * @param LocalFile       $file
78
     * @param string|string[] $fromText
79
     * @param string|string[] $toText
80
     * @param array           $options List of options:
81
     *                                 -postfix <string> (Default: replace) Set this to blank to replace inline
82
     *                                 -keepOldFile <bool> (Default: true)
83
     *
84
     * @throws InvalidArgumentException
85
     * @throws ProcessFailedException
86
     * @return LocalFile
87
     */
88 12
    public function replaceText(LocalFile $file, $fromText, $toText, array $options = [])
89
    {
90 12
        $this->options = $options;
91
92 12
        $postfix = $this->getOption('postfix', 'replace');
93 12
        $output = $this->getTargetFile($file, $postfix);
94
95 12
        $replacementString = $this->getReplacementString($fromText, $toText);
96
97 10
        if ($file->getFilename() == $output->getFilename()) {
98 1
            $cmd = sprintf(
99 1
                "perl -p -i -e '%s' %s",
100
                $replacementString,
101 1
                $file->getPath()
102
            );
103
        } else {
104 9
            $cmd = sprintf(
105 9
                "perl -p -e '%s' %s > %s",
106
                $replacementString,
107 9
                $file->getPath(),
108 9
                $output->getPath()
109
            );
110
        }
111
112 10
        $this->log(LogLevel::INFO, "Replacing the text: {from} to {to} in file: '{file}'", [
113 10
            'from' => json_encode($fromText),
114 10
            'to'   => json_encode($toText),
115 10
            'file' => $file,
116
        ]);
117
118 10
        return $this->processFile($file, $output, $cmd, $this->getOption('keepOldFile', true));
119
    }
120
121
    /**
122
     * @param string|string[] $from
123
     * @param string|string[] $to
124
     *
125
     * @return string
126
     */
127 12
    private function getReplacementString($from, $to)
128
    {
129 12
        if (is_array($from) && is_array($to)) {
130 3
            if (count($from) == count($to)) {
131 2
                $sedStrings = [];
132 2
                $fromSize = count($from);
133 2
                for ($i = 0; $i < $fromSize; $i++) {
134 2
                    $sedStrings[] = $this->getReplacementCommand($from[$i], $to[$i]);
135
                }
136 2
                return implode(';', $sedStrings);
137
            } else {
138 1
                throw new InvalidArgumentException("Number of items in 'fromText' (" . count($from) . ") is different to 'toText' (" . count($to) . ")");
139
            }
140 9
        } elseif (is_array($from) || is_array($to)) {
141 1
            throw new InvalidArgumentException("Only one of fromText or toText is an array, both should be arrays or string");
142
        } else {
143 8
            return $this->getReplacementCommand($from, $to);
144
        }
145
    }
146
147
    /**
148
     * Get the string replacement command for a single item
149
     *
150
     * @param string $fromText
151
     * @param string $toText
152
     *
153
     * @return string
154
     */
155 10
    private function getReplacementCommand($fromText, $toText)
156
    {
157 10
        $characters = ["'", ";", "\\", "/"];
158 10
        $escaped = ["\\'", "\\;", "\\\\", "\\/"];
159 10
        return sprintf(
160 10
            's/%s/%s/g',
161
            str_replace($characters, $escaped, $fromText),
162
            str_replace($characters, $escaped, $toText)
163
        );
164
    }
165
}
166