Passed
Push — master ( bbc3bd...e70b0b )
by Marwan
01:25
created

App::extend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * @author Marwan Al-Soltany <[email protected]>
5
 * @copyright Marwan Al-Soltany 2021
6
 * For the full copyright and license information, please view
7
 * the LICENSE file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace MAKS\Velox;
13
14
use MAKS\Velox\Backend\Config;
15
use MAKS\Velox\Backend\Router;
16
use MAKS\Velox\Backend\Globals;
17
use MAKS\Velox\Frontend\Data;
18
use MAKS\Velox\Frontend\View;
19
use MAKS\Velox\Frontend\HTML;
20
use MAKS\Velox\Frontend\Path;
21
use MAKS\Velox\Helper\Dumper;
22
use MAKS\Velox\Helper\Misc;
23
24
/**
25
 * A class that serves as a basic service-container for VELOX.
26
 * This class has all VELOX classes as public properties:
27
 *      - `$config`  = `Config::class`
28
 *      - `$router`  = `Router::class`
29
 *      - `$globals` = `Globals::class`
30
 *      - `$data`    = `Data::class`
31
 *      - `$view`    = `View::class`
32
 *      - `$html`    = `HTML::class`
33
 *      - `$path`    = `Path::class`
34
 *      - `$dumper`  = `Dumper::class`
35
 *      - `$misc`    = `Misc::class`
36
 *
37
 * Example:
38
 * ```
39
 * // create an instance
40
 * $app = new App();
41
 * // get an instance of the `Router` class via public property access notation
42
 * $app->router->handle('/dump', 'dd');
43
 * // or via calling a method with the same name
44
 * $app->router()->handle('/dump', 'dd');
45
 * ```
46
 *
47
 * @since 1.0.0
48
 */
49
class App
50
{
51
    public Config $config;
52
53
    public Router $router;
54
55
    public Globals $globals;
56
57
    public Data $data;
58
59
    public View $view;
60
61
    public HTML $html;
62
63
    public Path $path;
64
65
    public Dumper $dumper;
66
67
    public Misc $misc;
68
69
    protected array $methods;
70
71
72
    /**
73
     * Class constructor.
74
     */
75 5
    public function __construct()
76
    {
77 5
        $this->config  = new Config();
78 5
        $this->router  = new Router();
79 5
        $this->globals = new Globals();
80 5
        $this->data    = new Data();
81 5
        $this->view    = new View();
82 5
        $this->html    = new HTML();
83 5
        $this->path    = new Path();
84 5
        $this->dumper  = new Dumper();
85 5
        $this->misc    = new Misc();
86 5
        $this->methods = [];
87 5
    }
88
89 3
    public function __call(string $method, array $arguments)
90
    {
91 3
        $class = static::class;
92
93
        try {
94 3
            return isset($this->methods[$method]) ? $this->methods[$method](...$arguments) : $this->{$method};
95 1
        } catch (\Exception $error) {
96 1
            throw new \Exception(
97 1
                "Call to undefined method {$class}::{$method}()",
98 1
                (int)$error->getCode(),
99
                $error
100
            );
101
        }
102
    }
103
104 2
    public function __get(string $property)
105
    {
106 2
        $class = static::class;
107
108 2
        throw new \Exception("Call to undefined property {$class}::${$property}");
109
    }
110
111
112
    /**
113
     * Extends the class using the passed callback.
114
     *
115
     * @param string $name Method name.
116
     * @param callable $callback The callback to use as method body.
117
     *
118
     * @return callable The created bound closure.
119
     */
120 1
    public function extend(string $name, callable $callback): callable
121
    {
122 1
        $method = \Closure::fromCallable($callback);
123 1
        $method = \Closure::bind($method, $this, $this);
124
125 1
        return $this->methods[$name] = $method;
126
    }
127
128
129
    /**
130
     * Logs a message to a file and generates it if it does not exist.
131
     *
132
     * @param string $message The message wished to be logged.
133
     * @param array|null $context An associative array of values where array key = {key} in the message (context).
134
     * @param string|null $filename [optional] The name wished to be given to the file. If not provided `{global.logging.defaultFilename}` will be used instead.
135
     * @param string|null $directory [optional] The directory where the log file should be written. If not provided `{global.logging.defaultDirectory}` will be used instead.
136
     *
137
     * @return bool True on success (if message was written).
138
     */
139 4
    public static function log(string $message, ?array $context = [], ?string $filename = null, ?string $directory = null): bool
140
    {
141 4
        $logging = Config::get('global.logging');
142
143 4
        if (!$logging['enabled']) {
144 1
            return true;
145
        }
146
147 4
        $hasPassed = false;
148
149 4
        if (!$filename) {
150 1
            $filename = $logging['defaultFilename'];
151
        }
152
153 4
        if (!$directory) {
154 4
            $directory = $logging['defaultDirectory'];
155
        }
156
157 4
        $file = Path::normalize($directory, $filename, '.log');
158
159 4
        if (!file_exists($directory)) {
160 1
            mkdir($directory, 0744, true);
161
        }
162
163
        // create log file if it does not exist
164 4
        if (!is_file($file) && is_writable($directory)) {
165 2
            $signature = 'Created by ' . __METHOD__ . date('() \o\\n l jS \of F Y h:i:s A (Ymdhis)') . PHP_EOL . PHP_EOL;
166 2
            file_put_contents($file, $signature, 0);
167 2
            chmod($file, 0775);
168
        }
169
170
        // write in the log file
171 4
        if (is_writable($file)) {
172 4
            clearstatcache(true, $file);
173
            // empty the file if it exceeds 64MB
174 4
            if (filesize($file) > $logging['maxFileSize']) {
175 1
                $stream = fopen($file, 'r');
176 1
                if (is_resource($stream)) {
177 1
                    $signature = fgets($stream) . 'For exceeding the configured {global.logging.maxFileSize}, it was overwritten on ' . date('l jS \of F Y h:i:s A (Ymdhis)') . PHP_EOL . PHP_EOL;
178 1
                    fclose($stream);
179 1
                    file_put_contents($file, $signature, 0);
180 1
                    chmod($file, 0775);
181
                }
182
            }
183
184 4
            $timestamp = (new \DateTime())->format(DATE_ISO8601);
185 4
            $message   = Misc::interpolate($message, $context ?? []);
186
187 4
            $log = "$timestamp\t$message\n";
188
189 4
            $stream = fopen($file, 'a+');
190 4
            if (is_resource($stream)) {
191 4
                fwrite($stream, $log);
192 4
                fclose($stream);
193 4
                $hasPassed = true;
194
            }
195
        }
196
197 4
        return $hasPassed;
198
    }
199
}
200