Completed
Push — master ( c0a5a3...2f2f95 )
by Mārtiņš
02:20
created

MessageExtractor::extractFileMessages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 3
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
4
namespace Printful\GettextCms;
5
6
7
use Gettext\Extractors\Extractor;
8
use Gettext\Extractors\JsCode;
9
use Gettext\Extractors\PhpCode;
10
use Gettext\Extractors\VueJs;
11
use Gettext\Translations;
12
use League\Flysystem\Adapter\Local;
13
use League\Flysystem\Filesystem;
14
use League\Flysystem\Plugin\ListFiles;
15
use Printful\GettextCms\Exceptions\GettextCmsException;
16
use Printful\GettextCms\Exceptions\InvalidPathException;
17
use Printful\GettextCms\Exceptions\UnknownExtractorException;
18
use Printful\GettextCms\Interfaces\MessageConfigInterface;
19
use Printful\GettextCms\Structures\ScanItem;
20
21
class MessageExtractor
22
{
23
    const EXTRACTORS = [
24
        'js' => JsCode::class,
25
        'vue' => VueJs::class,
26
        'php' => PhpCode::class,
27
    ];
28
29
    /** @var MessageConfigInterface */
30
    private $config;
31
32
    /**
33
     * @param MessageConfigInterface $config
34
     */
35
    public function __construct(MessageConfigInterface $config)
36
    {
37
        $this->config = $config;
38
    }
39
40
    /**
41
     * @param ScanItem[] $items
42
     * @return Translations[] List of translation files extracted (for each domain)
43
     * @throws GettextCmsException
44
     */
45
    public function extract(array $items)
46
    {
47
        $defaultDomain = $this->config->getDefaultDomain();
48
        $domains = $this->config->getOtherDomains();
49
        $domains[] = $defaultDomain;
50
51
        $allTranslations = [];
52
53
        foreach ($domains as $domain) {
54
            $translations = new Translations;
55
56
            // When we scan for default domain, we have to specify an empty value
57
            // otherwise we would search for domain function calls with this domain
58
            // For example, empty domain will find string like __("message")
59
            // But if a domain is specified, it will look for dgettext("custom-domain", "message")
60
            if ($domain !== $defaultDomain) {
61
                $translations->setDomain($domain);
62
            }
63
64
            foreach ($items as $item) {
65
                // Scan for this item, translations will be merged with all domain translations
66
                $this->extractForDomain($item, $translations);
67
            }
68
69
            // Always set the domain even if it is the default one
70
            $translations->setDomain($domain);
71
72
            $allTranslations[] = $translations;
73
        }
74
75
        return $allTranslations;
76
    }
77
78
    /**
79
     * @param ScanItem $scanItem
80
     * @param Translations $translations
81
     * @return Translations
82
     * @throws InvalidPathException
83
     * @throws UnknownExtractorException
84
     */
85
    private function extractForDomain(ScanItem $scanItem, Translations $translations)
86
    {
87
        $pathnames = $this->resolvePathnames($scanItem);
88
89
        foreach ($pathnames as $pathname) {
90
            // Translations will be merged with given object
91
            $this->extractFileMessages($pathname, $translations, $scanItem->functions);
92
        }
93
94
        return $translations;
95
    }
96
97
    /**
98
     * @param $pathname
99
     * @param Translations $translations
100
     * @param array|null $functions Optional functions to scan for
101
     * @throws UnknownExtractorException
102
     */
103
    private function extractFileMessages($pathname, Translations $translations, array $functions = null)
104
    {
105
        $extractor = $this->getExtractor($pathname);
106
107
        $options = [
108
            'extractComments' => '', // This extracts comments above function call
109
            'domainOnly' => $translations->hasDomain(), // We scan for messages that match our needed domain only
110
        ];
111
112
        if ($functions) {
113
            $options['functions'] = $functions;
114
        }
115
116
        $extractor::fromFile($pathname, $translations, $options);
117
    }
118
119
    /**
120
     * Returns a list of files that match this item (if single file, then an array with a single pathname)
121
     *
122
     * @param ScanItem $item
123
     * @return array List of matching pathnames
124
     * @throws InvalidPathException
125
     */
126
    public function resolvePathnames(ScanItem $item): array
127
    {
128
        if (is_file($item->path)) {
129
            return [$item->path];
130
        }
131
132
        if (!is_dir($item->path)) {
133
            throw new InvalidPathException('Path "' . $item->path . '" does not exist');
134
        }
135
136
        return $this->resolveDirectoryFiles($item);
137
    }
138
139
    /**
140
     * If scan item is for a directory, this will create a list of matching files
141
     *
142
     * @param ScanItem $item
143
     * @return string[] List of pathnames to files
144
     */
145
    private function resolveDirectoryFiles(ScanItem $item)
146
    {
147
        $dir = realpath($item->path);
148
149
        $adapter = new Local($dir);
150
        $filesystem = new Filesystem($adapter);
151
        $filesystem->addPlugin(new ListFiles);
152
153
        $files = $filesystem->listFiles('', $item->recursive);
154
155
        // If extensions are set, filter other files out
156
        if ($item->extensions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $item->extensions of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
157
            $files = array_filter($files, function ($file) use ($item) {
158
                return in_array($file['extension'], $item->extensions);
159
            });
160
        }
161
162
        return array_map(function ($file) use ($dir) {
163
            return $dir . DIRECTORY_SEPARATOR . $file['path'];
164
        }, $files);
165
    }
166
167
    /**
168
     * @param string $pathname Full path to file
169
     * @return Extractor
170
     * @throws UnknownExtractorException
171
     */
172
    private function getExtractor($pathname)
173
    {
174
        $extension = pathinfo($pathname, PATHINFO_EXTENSION);
175
176
        if (isset(self::EXTRACTORS[$extension])) {
177
            return self::EXTRACTORS[$extension];
178
        }
179
180
        throw new UnknownExtractorException('Extractor is not know for file extension "' . $extension . '"');
181
    }
182
}