Completed
Pull Request — master (#7037)
by Damian
09:19
created

Environment   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 145
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 145
rs 10
c 0
b 0
f 0
wmc 23
lcom 2
cbo 1

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getVariables() 0 5 1
A setVariables() 0 6 2
A setMemoryLimitMax() 0 7 3
A getMemoryLimitMax() 0 7 2
B increaseTimeLimitTo() 0 19 6
A setTimeLimitMax() 0 4 1
A getTimeLimitMax() 0 4 1
C increaseMemoryLimitTo() 0 25 7
1
<?php
2
3
namespace SilverStripe\Core;
4
5
/**
6
 * Consolidates access and modification of PHP global variables and settings.
7
 * This class should be used sparingly, and only if information cannot be obtained
8
 * from a current {@link HTTPRequest} object.
9
 */
10
class Environment
11
{
12
    /**
13
     * Set maximum limit allowed for increaseMemoryLimit
14
     *
15
     * @var float|null
16
     */
17
    protected static $memoryLimitMax = null;
18
19
    /**
20
     * Set maximum limited allowed for increaseTimeLimit
21
     *
22
     * @var int|null
23
     */
24
    protected static $timeLimitMax = null;
25
26
    /**
27
     * Extract env vars prior to modification
28
     *
29
     * @return array List of all super globals
30
     */
31
    public static function getVariables()
0 ignored issues
show
Coding Style introduced by
getVariables uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
32
    {
33
        // Suppress return by-ref
34
        return array_merge($GLOBALS, []);
35
    }
36
37
    /**
38
     * Restore a backed up or modified list of vars to $globals
39
     *
40
     * @param array $vars
41
     */
42
    public static function setVariables(array $vars)
0 ignored issues
show
Coding Style introduced by
setVariables uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
43
    {
44
        foreach ($vars as $key => $value) {
45
            $GLOBALS[$key] = $value;
46
        }
47
    }
48
49
    /**
50
     * Increase the memory limit to the given level if it's currently too low.
51
     * Only increases up to the maximum defined in {@link setMemoryLimitMax()},
52
     * and defaults to the 'memory_limit' setting in the PHP configuration.
53
     *
54
     * @param string|float|int $memoryLimit A memory limit string, such as "64M".  If omitted, unlimited memory will be set.
55
     * @return bool true indicates a successful change, false a denied change.
56
     */
57
    public static function increaseMemoryLimitTo($memoryLimit = -1)
58
    {
59
        $memoryLimit = Convert::memstring2bytes($memoryLimit);
60
        $curLimit = Convert::memstring2bytes(ini_get('memory_limit'));
61
62
        // Can't go higher than infinite
63
        if ($curLimit < 0) {
64
            return true;
65
        }
66
67
        // Check hard maximums
68
        $max = static::getMemoryLimitMax();
69
        if ($max > 0 && ($memoryLimit < 0 || $memoryLimit > $max)) {
70
            $memoryLimit = $max;
71
        }
72
73
        // Increase the memory limit if it's too low
74
        if ($memoryLimit < 0) {
75
            ini_set('memory_limit', '-1');
76
        } elseif ($memoryLimit > $curLimit) {
77
            ini_set('memory_limit', Convert::bytes2memstring($memoryLimit));
78
        }
79
80
        return true;
81
    }
82
83
    /**
84
     * Set the maximum allowed value for {@link increaseMemoryLimitTo()}.
85
     * The same result can also be achieved through 'suhosin.memory_limit'
86
     * if PHP is running with the Suhosin system.
87
     *
88
     * @param string|float $memoryLimit Memory limit string or float value
89
     */
90
    static function setMemoryLimitMax($memoryLimit)
91
    {
92
        if (isset($memoryLimit) && !is_numeric($memoryLimit)) {
93
            $memoryLimit = Convert::memstring2bytes($memoryLimit);
94
        }
95
        static::$memoryLimitMax = $memoryLimit;
0 ignored issues
show
Documentation Bug introduced by
It seems like $memoryLimit can also be of type integer or string. However, the property $memoryLimitMax is declared as type double|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
96
    }
97
98
    /**
99
     * @return int Memory limit in bytes
100
     */
101
    public static function getMemoryLimitMax()
102
    {
103
        if (static::$memoryLimitMax === null) {
104
            return Convert::memstring2bytes(ini_get('memory_limit'));
105
        }
106
        return static::$memoryLimitMax;
107
    }
108
109
    /**
110
     * Increase the time limit of this script. By default, the time will be unlimited.
111
     * Only works if 'safe_mode' is off in the PHP configuration.
112
     * Only values up to {@link getTimeLimitMax()} are allowed.
113
     *
114
     * @param int $timeLimit The time limit in seconds.  If omitted, no time limit will be set.
115
     * @return Boolean TRUE indicates a successful change, FALSE a denied change.
116
     */
117
    public static function increaseTimeLimitTo($timeLimit = null)
118
    {
119
        // Check vs max limit
120
        $max = static::getTimeLimitMax();
121
        if ($max > 0 && $timeLimit > $max) {
122
            return false;
123
        }
124
125
        if (!$timeLimit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $timeLimit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
126
            set_time_limit(0);
127
        } else {
128
            $currTimeLimit = ini_get('max_execution_time');
129
            // Only increase if its smaller
130
            if ($currTimeLimit > 0 && $currTimeLimit < $timeLimit) {
131
                set_time_limit($timeLimit);
132
            }
133
        }
134
        return true;
135
    }
136
137
    /**
138
     * Set the maximum allowed value for {@link increaseTimeLimitTo()};
139
     *
140
     * @param int $timeLimit Limit in seconds
141
     */
142
    public static function setTimeLimitMax($timeLimit)
143
    {
144
        static::$timeLimitMax = $timeLimit;
145
    }
146
147
    /**
148
     * @return Int Limit in seconds
149
     */
150
    public static function getTimeLimitMax()
151
    {
152
        return static::$timeLimitMax;
153
    }
154
}
155