Config::getLogFile()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PjbServer\Tools\StandaloneServer;
6
7
use PjbServer\Tools\Exception;
8
9
/*
10
  java -Djava.awt.headless="true"
11
  -Dphp.java.bridge.threads=50
12
  -Dphp.java.bridge.base=/usr/lib/php/modules
13
  -Dphp.java.bridge.php_exec=/usr/local/bin/php-cgi
14
  -Dphp.java.bridge.default_log_file=
15
  -Dphp.java.bridge.default_log_level=5
16
  -Dphp.java.bridge.daemon="false"
17
  -jar JavaBridge.jar
18
 * sudo netstat -anltp|grep :8089
19
 */
20
21
class Config
22
{
23
    /**
24
     * Default configuration options.
25
     *
26
     * @var array<string, mixed>
27
     */
28
    protected $default_config = [
29
        'java_bin' => 'java',
30
        'server_jar' => '{base_dir}/resources/pjb713_standalone/JavaBridge.jar',
31
        'log_file' => '{base_dir}/var/pjbserver-port{tcp_port}.log',
32
        'pid_file' => '{base_dir}/var/pjbserver-port{tcp_port}.pid',
33
        'classpaths' => [],
34
        'threads' => 50
35
    ];
36
37
    /**
38
     * Internal configuration array.
39
     *
40
     * @var array<string, mixed>
41
     */
42
    protected $config;
43
44
    /**
45
     * Constructor.
46
     *
47
     * <code>
48
     *
49
     * $params = [
50
     *      // Port (required)
51
     *      'port' => 8089,
52
     *
53
     *      // Classpath autoloads (optional)
54
     *      'classpaths' => [
55
     *          '/my/path/to_specific/jar_file.jar',
56
     *          '/my/path/to_all_jars/*.jar'
57
     *      ],
58
     *
59
     *      'threads' => 50,
60
     *
61
     *      // Defaults (optional)
62
     *      'java_bin'   => 'java',
63
     *      'server_jar' => '{base_dir}/resources/pjb621_standalone/JavaBridge.jar',
64
     *      'log_file'   => '{base_dir}/var/pjbserver-port{tcp_port}.log',
65
     *      'pid_file'   => '{base_dir}/var/pjbserver-port{tcp_port}.pid'
66
     *
67
     * ];
68
     * $config = new StandaloneServer\Config($params);
69
     * </code>
70
     *
71
     * @throws Exception\InvalidArgumentException
72
     *
73 33
     * @param array<string, mixed> $config
74
     */
75 33
    public function __construct(array $config)
76 3
    {
77 30
        if (!isset($config['port'])) {
78 1
            throw new Exception\InvalidArgumentException("Error missing required 'port' in config");
79
        }
80 29
        if (!(bool) filter_var($config['port'], FILTER_VALIDATE_INT) || $config['port'] < 1) {
81
            throw new Exception\InvalidArgumentException("Option 'port' must be numeric greater than 0");
82 29
        }
83 29
        $port = $config['port'];
84 29
        // Substitute magic vars is deprecated and will be removed in v1.0.0
85 29
        $config = array_merge(
86 28
            $this->getDefaultConfig($port),
87 28
            $this->substitutePlaceholders($config, $port)
88
        );
89
        $this->checkConfig($config);
90
        $this->config = $config;
91
    }
92
93
    /**
94 19
     * Return port on which standalone server listens.
95
     */
96 19
    public function getPort(): int
97
    {
98
        return (int) $this->config['port'];
99
    }
100
101
    /**
102
     * Return jar file of the server.
103
     */
104 19
    public function getServerJar(): string
105
    {
106 19
        return $this->config['server_jar'];
107
    }
108
109
    /**
110
     * Return java binary.
111
     */
112
    public function getJavaBin(): string
113
    {
114 19
        return $this->config['java_bin'];
115
    }
116 19
117
    /**
118
     * Return log file.
119
     */
120
    public function getLogFile(): string
121
    {
122
        return $this->config['log_file'];
123
    }
124 19
125
    /**
126 19
     * Return an array containing the java classpath(s) for the server.
127
     *
128
     * @return string[]
129
     */
130
    public function getClasspaths(): array
131
    {
132
        return $this->config['classpaths'];
133
    }
134 19
135
    public function getPidFile(): string
136 19
    {
137
        return $this->config['pid_file'];
138
    }
139
140
    /**
141
     * Return standalone server threads.
142
     *
143
     * @return int|string
144 23
     */
145
    public function getThreads()
146 23
    {
147
        return $this->config['threads'];
148
    }
149
150
    /**
151
     * Return standalone configuration.
152
     *
153
     * @return array<string, mixed>
154 16
     */
155
    public function toArray(): array
156 16
    {
157
        return $this->config;
158
    }
159
160
    /**
161
     * Return standalone configuration.
162
     *
163
     * @deprecated use toArray() instead
164 5
     *
165
     * @return array<string, mixed>
166 5
     */
167
    public function getConfig(): array
168
    {
169
        return $this->toArray();
170
    }
171
172
    /**
173
     * Return default configuration options.
174
     *
175
     * @param int|string $port
176
     *
177
     * @return array<string, mixed>
178
     */
179
    protected function getDefaultConfig($port): array
180
    {
181
        return $this->substitutePlaceholders($this->default_config, $port);
182
    }
183
184
    /**
185
     * Substitute the placeholder {tcp_port} and {base_dir}
186
     * from a config array.
187
     *
188 29
     * @param array<string, mixed> $configArray associative array
189
     * @param int|string           $port
190 29
     *
191
     * @return array<string, mixed>
192
     */
193
    protected function substitutePlaceholders(array $configArray, $port): array
194
    {
195
        $substituted = [];
196
        $base_dir = $this->getBaseDir();
197
198
        foreach ($configArray as $key => $value) {
199
            if (is_string($value)) {
200
                $tmp = str_replace('{base_dir}', $base_dir, $value);
201 29
                $tmp = str_replace('{tcp_port}', (string) $port, $tmp);
202
            } else {
203 29
                $tmp = $value;
204 29
            }
205
            $substituted[$key] = $tmp;
206 29
        }
207 29
208 29
        return $substituted;
209 29
    }
210 29
211
    /**
212 29
     * Return pjbserver-tools installation base directory.
213
     *
214
     * @throws Exception\RuntimeException
215
     */
216
    public function getBaseDir(): string
217
    {
218
        // Four levels back.
219
        $ds = DIRECTORY_SEPARATOR;
220
        $dir = __DIR__ . "$ds..$ds..$ds..$ds..$ds";
221
        $base_dir = realpath($dir);
222 29
        if (!$base_dir) {
223
            $message = 'Cannot resolve project base directory.';
224
            throw new Exception\RuntimeException($message);
225 29
        }
226 29
227 29
        return $base_dir;
228 29
    }
229
230
    /**
231
     * Check configuration parameters.
232
     *
233 29
     * @throws Exception\InvalidArgumentException
234
     *
235
     * @param array<string, mixed> $config
236
     */
237
    protected function checkConfig(array $config): void
238
    {
239
        // Step 1: all required options
240
        $required = ['port', 'server_jar', 'log_file', 'pid_file', 'threads'];
241
        foreach ($required as $option) {
242
            if (!isset($config[$option]) || $config[$option] == '') {
243 29
                throw new Exception\InvalidArgumentException("Missing resuired configuration option: '$option''");
244
            }
245
        }
246 29
247 29
        // Step 2: server_jar file must exists
248 29
        if (!is_file($config['server_jar']) || !is_readable($config['server_jar'])) {
249
            throw new Exception\InvalidArgumentException("Server jar file not exists or unreadable. server-jar: '" . $config['server_jar'] . "'");
250
        }
251 29
252
        // Step 3: log and pid file should be creatable
253
        $temp_required_files = ['log_file', 'pid_file'];
254 29
        foreach ($temp_required_files as $option) {
255 1
            $file = $config[$option];
256
            $info = pathinfo($file);
257
            $dirname = $info['dirname'];
258
            if (!is_dir($dirname) || $dirname === '.') {
259 28
                $msg = "Option '$option' refer to an invalid or non-existent directory ($file)";
260 28
                throw new Exception\InvalidArgumentException($msg);
261 28
            }
262 28
            if (is_dir($file)) {
263 28
                $msg = "Option '$option' does not refer to a file but an existing directory ($file)";
264 28
                throw new Exception\InvalidArgumentException($msg);
265
            }
266
            if (file_exists($file) && !is_writable($file)) {
267
                $msg = "File specified in '$option' is not writable ($file)";
268 28
                throw new Exception\InvalidArgumentException($msg);
269
            }
270
        }
271
272 28
        // Step 4: Threads must be numeric greater than 0
273
274
        $threads = $config['threads'];
275
276 28
        if (!preg_match('/^([0-9])+$/', (string) $threads) || $threads <= 0) {
277
            $msg = "Parameter 'threads' must be valid integer greater than 0";
278
            throw new Exception\InvalidArgumentException($msg);
279
        }
280 28
281
        // Step 5: Java must be callable
282 28
283 1
        // @todo, many options exists
284 1
285
        // Step 6: Check classpaths autoload
286
        if (isset($config['classpaths'])) {
287
            if (!is_array($config['classpaths'])) {
288
                $msg = "Option 'classpaths' mus be a php array.";
289
                throw new Exception\InvalidArgumentException($msg);
290
            }
291
            foreach ($config['classpaths'] as $classpath) {
292 28
                if (preg_match('/\*\.jar$/', $classpath)) {
293 28
                    // Check if directory exists
294 1
                    $directory = preg_replace('/\*\.jar$/', '', $classpath);
295 1
                    if (!is_dir($directory) || !is_readable($directory)) {
296
                        $msg = "Classpath error, the directory of '$classpath' does not exists or is not readable";
297 28
                        throw new Exception\InvalidArgumentException($msg);
298 27
                    }
299
                } elseif (preg_match('/\.jar$/', $classpath)) {
300 27
                    // Check if file exists
301 27
                    if (!is_file($classpath) || !is_readable($classpath)) {
302 1
                        $msg = "Classpath error, the file '$classpath' does not exists or is not readable";
303 1
                        throw new Exception\InvalidArgumentException($msg);
304
                    }
305 27
                } else {
306
                    $msg = "Error in classpath, files to import must end by .jar extension ($classpath)";
307 1
                    throw new Exception\InvalidArgumentException($msg);
308 1
                }
309 1
            }
310
        }
311
    }
312
}
313