Passed
Push — master ( 4e42f9...eb61a8 )
by Jonathan
03:42
created

SourceCodeService::listMyArtJaubTranslations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * webtrees-mod-translationtool: MyArtJaub Translation Tool Module for webtrees
5
 *
6
 * @package MyArtJaub\Webtrees\Module
7
 * @subpackage TranslationTool
8
 * @author Jonathan Jaubart <[email protected]>
9
 * @copyright Copyright (c) 2020, Jonathan Jaubart
10
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
11
 */
12
13
declare(strict_types=1);
14
15
namespace MyArtJaub\Webtrees\Module\TranslationTool\Services;
16
17
use Composer\Package\PackageInterface;
18
use Fisharebest\Webtrees\Services\ModuleService;
19
use Gettext\Merge;
20
use Gettext\Scanner\PhpScanner;
21
use Gettext\Translations;
22
use Illuminate\Support\Collection;
23
use MyArtJaub\Webtrees\Module\AbstractModuleMaj;
24
25
/**
26
 * Service for extracting data from the webtrees and modules source code.
27
 */
28
class SourceCodeService
29
{
30
    
31
    /**
32
     * Gettext Translations merge strategy to be used - Use Theirs data
33
     * @var unknown
34
     */
35
    private const MERGE_STRATEGY_THEIRS = Merge::HEADERS_OVERRIDE
36
        | Merge::TRANSLATIONS_THEIRS
37
        | Merge::TRANSLATIONS_OVERRIDE
38
        | Merge::EXTRACTED_COMMENTS_THEIRS
39
        | Merge::REFERENCES_THEIRS
40
        | Merge::FLAGS_THEIRS
41
        | Merge::COMMENTS_THEIRS;
42
    
43
    /**
44
     * I18N functions to be looked for in the code
45
     * @var array
46
     */
47
    private const I18N_FUNCTIONS = [
48
        'translate' => 'gettext',
49
        'plural' => 'ngettext',
50
        'translateContext' => 'pgettext'
51
    ];
52
    
53
    /**
54
     * Lists all paths containing source code to be scanned for translations.
55
     * This contains the MyArtJaub modules's resources folder,
56
     * as well as MyArtJaub modules PSR-4 autoloading paths loaded through Composer
57
     *
58
     * @return Collection
59
     */
60
    public function sourceCodePaths(): Collection
61
    {
62
        $paths = app(ModuleService::class)->findByInterface(AbstractModuleMaj::class)
63
            ->mapWithKeys(function (AbstractModuleMaj $module) {
64
                return [$module->name() => [realpath($module->resourcesFolder())]];
65
            });
66
        
67
        $maj_packages = app(ComposerService::class)->listMyArtJaubPackagesPaths();
68
        
69
        foreach ($maj_packages as list($maj_package, $psr4_paths)) {
70
            /** @var PackageInterface $maj_package */
71
            $installer_name = $maj_package->getExtra()['installer-name'] ?? '';
72
            $key = $installer_name === '' ? $maj_package->getName() : '_' . $installer_name . '_';
73
            if (count($psr4_paths) > 0) {
74
                $paths->put($key, array_merge($paths->get($key, []), $psr4_paths));
75
            }
76
        }
77
        
78
        return $paths;
79
    }
80
    
81
    /**
82
     * Find all strings to be translated in PHP or PHTML files for a set of source code paths
83
     * The returned structure is a associated Collection with:
84
     *      - key: package/domain
85
     *      - value: Gettext Translations object for that domain
86
     *
87
     * @param Collection $source_code_paths
88
     * @return Collection
89
     */
90
    public function findStringsToTranslate(Collection $source_code_paths): Collection
91
    {
92
        $strings_to_translate = new Collection();
93
        foreach ($source_code_paths as $package => $paths) {
94
            $php_files = array();
95
            foreach ($paths as $path) {
96
                $php_files = array_merge($php_files, $this->glob_recursive($path . '/*.php') ?: array());
97
                $php_files = array_merge($php_files, $this->glob_recursive($path . '/*.phtml') ?: array());
98
            }
99
            
100
            $php_scanner = new PhpScanner(Translations::create($package));
101
            $php_scanner
102
                ->setFunctions(self::I18N_FUNCTIONS)
103
                ->ignoreInvalidFunctions(true);
104
            $php_scanner->setDefaultDomain($package);
105
            foreach ($php_files as $php_file) {
106
                $php_scanner->scanFile($php_file);
107
            }
108
            
109
            $strings_to_translate->put(
110
                $package,
111
                $strings_to_translate
112
                    ->get($package, Translations::create())
113
                    ->mergeWith($php_scanner->getTranslations()[$package], self::MERGE_STRATEGY_THEIRS)
114
            );
115
        }
116
        return $strings_to_translate;
117
    }
118
    
119
    /**
120
     * List all translations defined in MyArtJaub modules
121
     * The returned structure is a associated Collection with:
122
     *      - key: module name
123
     *      - value: array of translations for the module
124
     *
125
     * @param string $language
126
     * @return Collection
127
     */
128
    public function listMyArtJaubTranslations(string $language): Collection
129
    {
130
        return app(ModuleService::class)->findByInterface(AbstractModuleMaj::class)
131
            ->mapWithKeys(function (AbstractModuleMaj $module) use ($language) {
132
                return [$module->name() => $module->customTranslations($language)];
133
            });
134
    }
135
    
136
    /**
137
     * Extension of the standard PHP glob function to apply it recursively.
138
     *
139
     * @param string $pattern
140
     * @param int $flags
141
     * @return string[]
142
     * @see glob()
143
     * @phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
144
     */
145
    protected function glob_recursive(string $pattern, int $flags = 0): array
146
    {
147
        $files = glob($pattern, $flags) ?: [];
148
        $dirs = glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) ?: [];
149
        
150
        foreach ($dirs as $dir) {
151
            $files = array_merge($files, $this->glob_recursive($dir . '/' . basename($pattern), $flags));
152
        }
153
        
154
        return $files;
155
    }
156
}
157