1<?php
2
3namespace Gettext\Utils;
4
5use Exception;
6use Gettext\Translations;
7
8abstract class FunctionsScanner
9{
10    /**
11     * Scan and returns the functions and the arguments.
12     *
13     * @param array $constants Constants used in the code to replace
14     *
15     * @return array
16     */
17    abstract public function getFunctions(array $constants = []);
18
19    /**
20     * Search for specific functions and create translations.
21     *
22     * You can pass multiple translation with different domains and value found will be sorted respectively.
23     *
24     * @param Translations|Translations[] $translations Multiple domain translations instances where to save the values
25     * @param array $options The extractor options
26     * @throws Exception
27     */
28    public function saveGettextFunctions($translations, array $options)
29    {
30        $translations = is_array($translations) ? $translations : [$translations];
31
32        /** @var Translations[] $translationByDomain [domain => translations, ..] */
33        $translationByDomain = array_reduce($translations, function ($carry, Translations $translations) {
34            $carry[$translations->getDomain()] = $translations;
35            return $carry;
36        }, []);
37
38        $functions = $options['functions'];
39        $file = $options['file'];
40
41        foreach ($this->getFunctions($options['constants']) as $function) {
42            list($name, $line, $args) = $function;
43
44            if (isset($options['lineOffset'])) {
45                $line += $options['lineOffset'];
46            }
47
48            if (!isset($functions[$name])) {
49                continue;
50            }
51
52            $deconstructed = $this->deconstructArgs($functions[$name], $args);
53
54            if (!$deconstructed) {
55                continue;
56            }
57
58            list($domain, $context, $original, $plural) = $deconstructed;
59
60            if ((string)$original === '') {
61                continue;
62            }
63
64            $isDefaultDomain = $domain === null;
65
66            $domainTranslations = isset($translationByDomain[$domain]) ? $translationByDomain[$domain] : false;
67
68            if (!empty($options['domainOnly']) && $isDefaultDomain) {
69                // If we want to find translations for a specific domain, skip default domain messages
70                continue;
71            }
72
73            if (!$domainTranslations) {
74                continue;
75            }
76
77            $translation = $domainTranslations->insert($context, $original, $plural);
78            $translation->addReference($file, $line);
79
80            if (isset($function[3])) {
81                foreach ($function[3] as $extractedComment) {
82                    $translation->addExtractedComment($extractedComment);
83                }
84            }
85        }
86    }
87
88    /**
89     * Deconstruct arguments to translation values
90     *
91     * @param $function
92     * @param $args
93     * @return array|null
94     * @throws Exception
95     */
96    protected function deconstructArgs($function, $args)
97    {
98        $domain = null;
99        $context = null;
100        $original = null;
101        $plural = null;
102
103        switch ($function) {
104            case 'noop':
105            case 'gettext':
106                if (!isset($args[0])) {
107                    return null;
108                }
109
110                $original = $args[0];
111                break;
112            case 'ngettext':
113                if (!isset($args[1])) {
114                    return null;
115                }
116
117                list($original, $plural) = $args;
118                break;
119            case 'pgettext':
120                if (!isset($args[1])) {
121                    return null;
122                }
123
124                list($context, $original) = $args;
125                break;
126            case 'dgettext':
127                if (!isset($args[1])) {
128                    return null;
129                }
130
131                list($domain, $original) = $args;
132                break;
133            case 'dpgettext':
134                if (!isset($args[2])) {
135                    return null;
136                }
137
138                list($domain, $context, $original) = $args;
139                break;
140            case 'npgettext':
141                if (!isset($args[2])) {
142                    return null;
143                }
144
145                list($context, $original, $plural) = $args;
146                break;
147            case 'dnpgettext':
148                if (!isset($args[3])) {
149                    return null;
150                }
151
152                list($domain, $context, $original, $plural) = $args;
153                break;
154            case 'dngettext':
155                if (!isset($args[2])) {
156                    return null;
157                }
158
159                list($domain, $original, $plural) = $args;
160                break;
161            default:
162                throw new Exception(sprintf('Not valid function %s', $function));
163        }
164
165        return [$domain, $context, $original, $plural];
166    }
167}
168