Completed
Pull Request — master (#3)
by Harry
05:31
created

ReplaceText::replaceText()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 47
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 6

Importance

Changes 5
Bugs 2 Features 0
Metric Value
c 5
b 2
f 0
dl 0
loc 47
ccs 37
cts 37
cp 1
rs 8.5125
cc 6
eloc 32
nc 5
nop 4
crap 6
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\GetOptionTrait;
17
use Graze\DataFile\Helper\OptionalLoggerTrait;
18
use Graze\DataFile\Helper\Process\ProcessFactoryAwareInterface;
19
use Graze\DataFile\Node\FileNodeInterface;
20
use Graze\DataFile\Node\LocalFile;
21
use InvalidArgumentException;
22
use Psr\Log\LoggerAwareInterface;
23
use Psr\Log\LogLevel;
24
use Symfony\Component\Process\Exception\ProcessFailedException;
25
26
class ReplaceText implements FileModifierInterface, LoggerAwareInterface, ProcessFactoryAwareInterface
27
{
28
    use OptionalLoggerTrait;
29
    use FileProcessTrait;
30
    use GetOptionTrait;
31
32
    /**
33
     * Can this file be modified by this modifier
34
     *
35
     * @param FileNodeInterface $file
36
     *
37
     * @return bool
38
     */
39 1
    public function canModify(FileNodeInterface $file)
40
    {
41
        return (
42 1
            ($file instanceof localFile) &&
43 1
            ($file->exists())
44 1
        );
45
    }
46
47
    /**
48
     * Modify the file
49
     *
50
     * @param FileNodeInterface $file
51
     * @param array             $options List of options:
52
     *                                   -fromText <string|array> Text to be replace
53
     *                                   -toText <string|array> Text to replace
54
     *                                   -postifx <string> (Default: replace) Set this to blank to replace inline
55
     *                                   -keepOldFile <bool> (Default: true)
56
     *
57
     * @return FileNodeInterface
58
     */
59 5
    public function modify(FileNodeInterface $file, array $options = [])
60
    {
61 5
        $this->options = $options;
62 5
        $fromText = $this->requireOption('fromText');
63 4
        $toText = $this->requireOption('toText');
64
65 3
        unset($options['fromText']);
66 3
        unset($options['toText']);
67
68 3
        if (!($file instanceof LocalFile)) {
69 1
            throw new InvalidArgumentException("Supplied: $file is not a LocalFile");
70
        }
71
72 2
        return $this->replaceText($file, $fromText, $toText, $options);
73
    }
74
75
    /**
76
     * @extend Graze\DataFile\Node\File\LocalFile
77
     *
78
     * @param LocalFile       $file
79
     * @param string|string[] $fromText
80
     * @param string|string[] $toText
81
     * @param array           $options List of options:
82
     *                                 -postfix <string> (Default: replace) Set this to blank to replace inline
83
     *                                 -keepOldFile <bool> (Default: true)
84
     *
85
     * @throws InvalidArgumentException
86
     * @throws ProcessFailedException
87
     * @return LocalFile
88
     */
89 11
    public function replaceText(LocalFile $file, $fromText, $toText, array $options = [])
90
    {
91 11
        $this->options = $options;
92
93 11
        $postfix = $this->getOption('postfix', 'replace');
94 11
        $output = $this->getTargetFile($file, $postfix);
95
96 11
        if (is_array($fromText)) {
97 3
            if (is_array($toText) &&
98 3
                count($fromText) == count($toText)
99 3
            ) {
100 2
                $sedStrings = [];
101 2
                $fromSize = count($fromText);
102 2
                for ($i = 0; $i < $fromSize; $i++) {
103 2
                    $sedStrings[] = $this->getReplacementCommand($fromText[$i], $toText[$i]);
104 2
                }
105 2
                $replacementString = implode(';', $sedStrings);
106 2
            } else {
107 1
                throw new InvalidArgumentException("Number of items in 'fromText' (" . count($fromText) . ") is different to 'toText' (" . count($toText) . ")");
108
            }
109 2
        } else {
110 8
            $replacementString = $this->getReplacementCommand($fromText, $toText);
0 ignored issues
show
Bug introduced by
It seems like $toText defined by parameter $toText on line 89 can also be of type array<integer,string>; however, Graze\DataFile\Modify\Re...getReplacementCommand() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
111
        }
112
113 10
        if ($file->getFilename() == $output->getFilename()) {
114 1
            $cmd = sprintf(
115 1
                "perl -p -i -e '%s' %s",
116 1
                $replacementString,
117 1
                $file->getPath()
118 1
            );
119 1
        } else {
120 9
            $cmd = sprintf(
121 9
                "perl -p -e '%s' %s > %s",
122 9
                $replacementString,
123 9
                $file->getPath(),
124 9
                $output->getPath()
125 9
            );
126
        }
127
128 10
        $this->log(LogLevel::INFO, "Replacing the text: {from} to {to} in file: '{file}'", [
129 10
            'from' => json_encode($fromText),
130 10
            'to'   => json_encode($toText),
131 10
            'file' => $file,
132 10
        ]);
133
134 10
        return $this->processFile($file, $output, $cmd, $this->getOption('keepOldFile', true));
135
    }
136
137
    /**
138
     * Get the string replacement command for a single item
139
     *
140
     * @param string $fromText
141
     * @param string $toText
142
     *
143
     * @return string
144
     */
145 10
    private function getReplacementCommand($fromText, $toText)
146
    {
147 10
        return sprintf(
148 10
            's/%s/%s/g',
149 10
            str_replace(["'", ";", "\\"], ["\\'", "\\;", "\\\\"], $fromText),
150 10
            str_replace(["'", ";", "\\"], ["\\'", "\\;", "\\\\"], $toText)
151 10
        );
152
    }
153
}
154