eval.php$0 ➔ run()   D
last analyzed

Complexity

Conditions 15

Size

Total Lines 111

Duplication

Lines 0
Ratio 0 %

Importance

Changes 16
Bugs 0 Features 0
Metric Value
dl 0
loc 111
c 16
b 0
f 0
cc 15
rs 4.7333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Livia
4
 * Copyright 2017-2019 Charlotte Dunois, All Rights Reserved
5
 *
6
 * Website: https://charuru.moe
7
 * License: https://github.com/CharlotteDunois/Livia/blob/master/LICENSE
8
*/
9
10
return function ($client) {
11
    return (new class($client) extends \CharlotteDunois\Livia\Commands\Command {
12
        protected $timeformats = array('ns', 'µs', 'ms');
13
        protected $lastResult;
14
        
15
        /** @var \Symfony\Component\VarDumper\Cloner\VarCloner */
16
        protected $cloner;
17
        
18
        /** @var \Symfony\Component\VarDumper\Dumper\CliDumper */
19
        protected $dumper;
20
        
21
        /** @var bool */
22
        protected $xdebug;
23
        
24
        function __construct(\CharlotteDunois\Livia\Client $client) {
25
            parent::__construct($client, array(
26
                'name' => 'eval',
27
                'aliases' => array(),
28
                'group' => 'utils',
29
                'description' => 'Executes PHP code.',
30
                'guildOnly' => false,
31
                'ownerOnly' => true,
32
                'argsSingleQuotes' => false,
33
                'args' => array(
34
                    array(
35
                        'key' => 'script',
36
                        'prompt' => 'What is the fancy code you wanna run?',
37
                        'type' => 'string'
38
                    )
39
                ),
40
                'guarded' => true
41
            ));
42
            
43
            $this->cloner = new \Symfony\Component\VarDumper\Cloner\VarCloner();
44
            $this->dumper = new \Symfony\Component\VarDumper\Dumper\CliDumper();
45
            $this->xdebug = \extension_loaded('xdebug');
46
            
47
            $this->cloner->setMinDepth(1);
48
            $this->cloner->setMaxItems(0);
49
            $this->dumper->setColors(false);
50
        }
51
        
52
        function run(\CharlotteDunois\Livia\Commands\Context $context, \ArrayObject $args, bool $fromPattern) {
53
            $messages = array();
54
            $prev = null;
55
            
56
            $code = $args['script'];
57
            if(\mb_substr($code, -1) !== ';') {
58
                $code .= ';';
59
            }
60
            
61
            if(\mb_strpos($code, 'return') === false && \mb_strpos($code, 'echo') === false) {
62
                $code = \explode(';', $code);
63
                $code[(\count($code) - 2)] = \PHP_EOL.'return '.\trim($code[(\count($code) - 2)]);
64
                $code = \implode(';', $code);
65
            }
66
            
67
            return (new \React\Promise\Promise(function (callable $resolve, callable $reject) use ($context, $args, $code, &$messages, &$prev) {
68
                $time = null;
69
                
70
                $timer = function (bool $callback = false) {
71
                    static $hrtime;
72
                    return $this->timer($hrtime, $callback);
73
                };
74
                
75
                $doCallback = function ($result) use ($code, $context, &$messages, &$time, &$timer) {
76
                    $endtime = $timer(true);
77
                    
78
                    $previous = \set_error_handler(array($this, 'errorCallback'));
79
                    $result = $this->invokeDump($result);
80
                    \set_error_handler($previous);
81
                    
82
                    $len = \mb_strlen($result);
83
                    $maxlen = 1850 - \mb_strlen($code);
84
                    
85
                    if($len > $maxlen) {
86
                        $result = \mb_substr($result, 0, $maxlen).\PHP_EOL.'...';
87
                    }
88
                    
89
                    $sizeformat = \count($this->timeformats) - 1;
90
                    $format = 0;
91
                    
92
                    $exectime = $endtime - $time;
93
                    while(\ceil($exectime) >= 1000.0 && $format < $sizeformat) {
94
                        $exectime /= 1000;
95
                        $format++;
96
                    }
97
                    $exectime = \ceil($exectime);
98
                    
99
                    $messages[] = $context->say($context->message->author.\CharlotteDunois\Yasmin\Models\Message::$replySeparator.'Executed callback after '.$exectime.$this->timeformats[$format].'.'.\PHP_EOL.\PHP_EOL.'```php'.\PHP_EOL.$result.\PHP_EOL.'```'.($len > $maxlen ? \PHP_EOL.'Original length: '.$len : ''));
100
                };
101
                
102
                $prev = \set_error_handler(array($this, 'errorCallback'));
103
                
104
                $endtime = null;
105
                $time = $timer();
106
                
107
                $evalcode = 'namespace CharlotteDunois\\Livia\\Commands\\EvalNamespace\\'.\preg_replace('/[^a-z]/i', '', \bin2hex(\random_bytes(10)).\sha1(\time())).';'.
108
                                \PHP_EOL.$code;
109
                
110
                $result = (function () use ($evalcode, $context, &$doCallback) {
111
                    $client = $this->client;
112
                    return eval($evalcode);
113
                })();
114
                
115
                if(!($result instanceof \React\Promise\PromiseInterface)) {
116
                    $endtime = $timer();
117
                    $result = \React\Promise\resolve($result);
118
                }
119
                
120
                return $result->then(function ($result) use ($code, $context, &$messages, &$prev, &$endtime, $time, &$timer) {
121
                    if($endtime === null) {
122
                        $endtime = $timer();
123
                    }
124
                    
125
                    $this->lastResult = $result;
126
                    $result = $this->invokeDump($result);
127
                    
128
                    \set_error_handler($prev);
129
                    
130
                    $len = \mb_strlen($result);
131
                    $maxlen = 1850 - \mb_strlen($code);
132
                    
133
                    if($len > $maxlen) {
134
                        $result = \mb_substr($result, 0, $maxlen).\PHP_EOL.'...';
135
                    }
136
                    
137
                    $sizeformat = \count($this->timeformats) - 1;
138
                    $format = 0;
139
                    
140
                    $exectime = $endtime - $time;
141
                    while(\ceil($exectime) >= 1000.0 && $format < $sizeformat) {
142
                        $exectime /= 1000;
143
                        $format++;
144
                    }
145
                    $exectime = \ceil($exectime);
146
                    
147
                    $messages[] = $context->say($context->message->author.\CharlotteDunois\Yasmin\Models\Message::$replySeparator.'Executed in '.$exectime.$this->timeformats[$format].'.'.\PHP_EOL.\PHP_EOL.'```php'.\PHP_EOL.$result.\PHP_EOL.'```'.($len > $maxlen ? \PHP_EOL.'Original length: '.$len : ''));
148
                    return $messages;
149
                })->done($resolve, $reject);
150
            }))->otherwise(function ($e) use ($code, $context, &$messages, &$prev) {
151
                \set_error_handler($prev);
152
                
153
                $e = (string) $e;
154
                $len = \mb_strlen($e);
155
                $maxlen = 1900 - \mb_strlen($code);
156
                
157
                if($len > $maxlen) {
158
                    $e = \mb_substr($e, 0, $maxlen).\PHP_EOL.'...';
159
                }
160
                
161
                $messages[] = $context->say($context->message->author.\PHP_EOL.'```php'.\PHP_EOL.$code.\PHP_EOL.'```'.\PHP_EOL.'Error: ```'.\PHP_EOL.$e.\PHP_EOL.'```');
162
                return $messages;
163
            });
164
        }
165
        
166
        /**
167
         * @param mixed  $result
168
         * @return string
169
         * @throws \RuntimeException
170
         */
171
        function invokeDump($result) {
172
            if($this->xdebug) {
173
                \ob_start('mb_output_handler');
174
                
175
                $old = \ini_get('xdebug.var_display_max_depth');
176
                \ini_set('xdebug.var_display_max_depth', 1);
177
                
178
                \xdebug_var_dump($result);
179
                \ini_set('xdebug.var_display_max_depth', $old);
180
                $result = @\ob_get_clean(); // Fixes a xdebug bug "Cannot modify header information"
181
                
182
                $result = \substr($result, (\strpos($result, "\n") + 1));
183
                $result = \preg_replace("/#(\d+) \((\d+)\) {\n\s*...\n\s*}/u", '#$1 ($2) { }', $result);
184
            } else {
185
                $data = $this->cloner->cloneVar($result);
186
                $result = $this->dumper->dump($data->withMaxDepth(1), true);
187
            }
188
            
189
            $email = $this->client->user->email;
190
            $tokenregex = \preg_quote($this->client->token, '/');
191
            $emailregex = (!empty($email) ? \preg_quote($email, '/') : null);
192
            
193
            $result = \preg_replace('/string\(\d+\) "'.$tokenregex.'"'.($emailregex !== null ? '|string\(\d+\) "'.$emailregex.'"' : '').'/iu', 'string(10) "[redacted]"', $result);
194
            $result = \preg_replace('/'.$tokenregex.($emailregex !== null ? '|'.$emailregex : '').'/iu', '[redacted]', $result);
195
            
196
            return $result;
197
        }
198
        
199
        /**
200
         * @return bool
201
         * @throws \ErrorException
202
         */
203
        function errorCallback($errno, $errstr, $errfile, $errline) {
204
            // Latter fixes a bug
205
            if(\error_reporting() === 0) {
206
                return true;
207
            }
208
            
209
            throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
210
        }
211
        
212
        /**
213
         * @return mixed
214
         */
215
        function timer(?\CharlotteDunois\Livia\Utils\HRTimer &$hrtime, bool $callback = false) {
216
            if(!$hrtime) {
217
                $hrtime = new \CharlotteDunois\Livia\Utils\HRTimer();
218
                return ($hrtime->start() ?? 0);
219
            }
220
            
221
            if($callback) {
222
                return $hrtime->time();
223
            }
224
            
225
            return $hrtime->stop();
226
        }
227
    });
228
};
229