Issues (85)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Str.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @package    Fuel\Common
4
 * @version    2.0
5
 * @author     Fuel Development Team
6
 * @license    MIT License
7
 * @copyright  2010 - 2015 Fuel Development Team
8
 * @link       http://fuelphp.com
9
 */
10
11
namespace Fuel\Common;
12
13
/**
14
 * String handling with encoding support
15
 *
16
 * PHP needs to be compiled with --enable-mbstring
17
 * or a fallback without encoding support is used
18
 */
19
class Str
20
{
21
	protected $encoding = 'UTF-8';
22
23
  /**
24
	 * Truncates a string to the given length.  It will optionally preserve
25
	 * HTML tags if $is_html is set to true.
26
	 *
27
	 * @param   string  $string        the string to truncate
28
	 * @param   int     $limit         the number of characters to truncate too
29
	 * @param   string  $continuation  the string to use to denote it was truncated
30
	 * @param   bool    $is_html       whether the string has HTML
31
	 * @return  string  the truncated string
32
	 */
33
	public function truncate($string, $limit, $continuation = '...', $is_html = false)
34
	{
35
		$offset = 0;
36
		$tags = array();
37
		if ($is_html)
38
		{
39
			// Handle special characters.
40
			preg_match_all('/&[a-z]+;/i', strip_tags($string), $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
41
			foreach ($matches as $match)
0 ignored issues
show
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
42
			{
43
				if ($match[0][1] >= $limit)
44
				{
45
					break;
46
				}
47
				$limit += ($this->length($match[0][0]) - 1);
48
			}
49
50
			// Handle all the html tags.
51
			preg_match_all('/<[^>]+>([^<]*)/', $string, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
52
			foreach ($matches as $match)
0 ignored issues
show
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
53
			{
54
				if($match[0][1] - $offset >= $limit)
55
				{
56
					break;
57
				}
58
				$tag = $this->sub(strtok($match[0][0], " \t\n\r\0\x0B>"), 1);
59
				if($tag[0] != '/')
60
				{
61
					$tags[] = $tag;
62
				}
63
				elseif (end($tags) == $this->sub($tag, 1))
64
				{
65
					array_pop($tags);
66
				}
67
				$offset += $match[1][1] - $match[0][1];
68
			}
69
		}
70
		$new_string = $this->sub($string, 0, $limit = min($this->length($string),  $limit + $offset));
71
		$new_string .= ($this->length($string) > $limit ? $continuation : '');
72
		$new_string .= (count($tags = array_reverse($tags)) ? '</'.implode('></',$tags).'>' : '');
73
		return $new_string;
74
	}
75
76
	/**
77
	 * Add's _1 to a string or increment the ending number to allow _2, _3, etc
78
	 *
79
	 * @param   string  $str  required
80
	 * @return  string
81
	 */
82
	public function increment($str, $first = 1, $separator = '_')
83
	{
84
		preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match);
85
86
		return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first;
87
	}
88
89
	/**
90
	 * Checks wether a string has a precific beginning.
91
	 *
92
	 * @param   string   $str          string to check
93
	 * @param   string   $start        beginning to check for
94
	 * @param   boolean  $ignore_case  wether to ignore the case
95
	 * @return  boolean  wether a string starts with a specified beginning
96
	 */
97
	public function startsWith($str, $start, $ignore_case = false)
98
	{
99
		return (bool) preg_match('/^'.preg_quote($start, '/').'/m'.($ignore_case ? 'i' : ''), $str);
100
	}
101
102
	/**
103
	 * Checks wether a string has a precific ending.
104
	 *
105
	 * @param   string   $str          string to check
106
	 * @param   string   $end          ending to check for
107
	 * @param   boolean  $ignore_case  wether to ignore the case
108
	 * @return  boolean  wether a string ends with a specified ending
109
	 */
110
	public function endsWith($str, $end, $ignore_case = false)
111
	{
112
		return (bool) preg_match('/'.preg_quote($end, '/').'$/m'.($ignore_case ? 'i' : ''), $str);
113
	}
114
115
	/**
116
	 * substr
117
	 *
118
	 * @param   string    $str       required
119
	 * @param   int       $start     required
120
	 * @param   int|null  $length
121
	 * @param   string    $encoding  default UTF-8
122
	 * @return  string
123
	 */
124
	public function sub($str, $start, $length = null, $encoding = null)
125
	{
126
		$encoding or $encoding = $this->encoding;
127
128
		// substr functions don't parse null correctly
129
		$length = is_null($length) ? (function_exists('mb_substr') ? mb_strlen($str, $encoding) : strlen($str)) - $start : $length;
130
131
		return function_exists('mb_substr')
132
			? mb_substr($str, $start, $length, $encoding)
133
			: substr($str, $start, $length);
134
	}
135
136
	/**
137
	 * strlen
138
	 *
139
	 * @param   string  $str       required
140
	 * @param   string  $encoding  default UTF-8
141
	 * @return  int
142
	 */
143
	public function length($str, $encoding = null)
144
	{
145
		$encoding or $encoding = $this->encoding;
146
147
		return function_exists('mb_strlen')
148
			? mb_strlen($str, $encoding)
149
			: strlen($str);
150
	}
151
152
	/**
153
	 * lower
154
	 *
155
	 * @param   string  $str       required
156
	 * @param   string  $encoding  default UTF-8
157
	 * @return  string
158
	 */
159
	public function lower($str, $encoding = null)
160
	{
161
		$encoding or $encoding = $this->encoding;
162
163
		return function_exists('mb_strtolower')
164
			? mb_strtolower($str, $encoding)
165
			: strtolower($str);
166
	}
167
168
	/**
169
	 * upper
170
	 *
171
	 * @param   string  $str       required
172
	 * @param   string  $encoding  default UTF-8
173
	 * @return  string
174
	 */
175
	public function upper($str, $encoding = null)
176
	{
177
		$encoding or $encoding = $this->encoding;
178
179
		return function_exists('mb_strtoupper')
180
			? mb_strtoupper($str, $encoding)
181
			: strtoupper($str);
182
	}
183
184
	/**
185
	 * lcfirst
186
	 *
187
	 * Does not strtoupper first
188
	 *
189
	 * @param   string  $str       required
190
	 * @param   string  $encoding  default UTF-8
191
	 * @return  string
192
	 */
193 View Code Duplication
	public function lcfirst($str, $encoding = null)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
	{
195
		$encoding or $encoding = $this->encoding;
196
197
		return function_exists('mb_strtolower')
198
			? mb_strtolower(mb_substr($str, 0, 1, $encoding), $encoding).
199
				mb_substr($str, 1, mb_strlen($str, $encoding), $encoding)
200
			: lcfirst($str);
201
	}
202
203
	/**
204
	 * ucfirst
205
	 *
206
	 * Does not strtolower first
207
	 *
208
	 * @param   string $str       required
209
	 * @param   string $encoding  default UTF-8
210
	 * @return   string
211
	 */
212 View Code Duplication
	public function ucfirst($str, $encoding = null)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
213
	{
214
		$encoding or $encoding = $this->encoding;
215
216
		return function_exists('mb_strtoupper')
217
			? mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding).
218
				mb_substr($str, 1, mb_strlen($str, $encoding), $encoding)
219
			: ucfirst($str);
220
	}
221
222
	/**
223
	 * ucwords
224
	 *
225
	 * First strtolower then ucwords
226
	 *
227
	 * ucwords normally doesn't strtolower first
228
	 * but MB_CASE_TITLE does, so ucwords now too
229
	 *
230
	 * @param   string   $str       required
231
	 * @param   string   $encoding  default UTF-8
232
	 * @return  string
233
	 */
234
	public function ucwords($str, $encoding = null)
235
	{
236
		$encoding or $encoding = $this->encoding;
237
238
		return function_exists('mb_convert_case')
239
			? mb_convert_case($str, MB_CASE_TITLE, $encoding)
240
			: ucwords(strtolower($str));
241
	}
242
243
	/**
244
	  * Creates a random string of characters
245
	  *
246
	  * @param   string  the type of string
247
	  * @param   int     the number of characters
248
	  * @return  string  the random string
249
	  */
250
	public function random($type = 'alnum', $length = 16)
251
	{
252
		switch($type)
253
		{
254
			case 'basic':
255
				$result = mt_rand();
256
				break;
257
258
			case 'unique':
259
				$result = md5(uniqid(mt_rand()));
260
				break;
261
262
			case 'sha1' :
263
				$result = sha1(uniqid(mt_rand(), true));
264
				break;
265
266
			default:
267
			case 'alnum':
0 ignored issues
show
case 'alnum': does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
268
			case 'numeric':
269
			case 'nozero':
270
			case 'alpha':
271
			case 'distinct':
272
			case 'hexdec':
273
				switch ($type)
274
				{
275
					case 'alpha':
276
						$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
277
						break;
278
279
					case 'numeric':
280
						$pool = '0123456789';
281
						break;
282
283
					case 'nozero':
284
						$pool = '123456789';
285
						break;
286
287
					case 'distinct':
288
						$pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
289
						break;
290
291
					case 'hexdec':
292
						$pool = '0123456789abcdef';
293
						break;
294
295
					default:
296
					case 'alnum':
0 ignored issues
show
case 'alnum': $pool ...FGHIJKLMNOPQRSTUVWXYZ'; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
297
						$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
298
				}
299
300
				$result = '';
301
				for ($i=0; $i < $length; $i++)
302
				{
303
					$result .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
304
				}
305
		}
306
307
		return $result;
308
	}
309
310
	/**
311
	 * Returns a closure that will alternate between the args which to return.
312
	 * If you call the closure with false as the arg it will return the value without
313
	 * alternating the next time.
314
	 *
315
	 * @return \Closure
316
	 */
317
	public function alternator()
318
	{
319
		// the args are the values to alternate
320
		$args = func_get_args();
321
322
		return function ($next = true) use ($args)
323
		{
324
			static $i = 0;
325
			return $args[($next ? $i++ : $i) % count($args)];
326
		};
327
	}
328
329
	/**
330
	 * Parse the params from a string using strtr()
331
	 *
332
	 * @param   string  string to parse
333
	 * @param   array   params to str_replace
334
	 * @return  string
335
	 */
336
	public function tr($string, $array = array())
337
	{
338
		if (is_string($string))
339
		{
340
			$tr_arr = array();
341
342
			foreach ($array as $from => $to)
343
			{
344
				substr($from, 0, 1) !== ':' and $from = ':'.$from;
345
				$tr_arr[$from] = $to;
346
			}
347
			unset($array);
348
349
			return strtr($string, $tr_arr);
350
		}
351
		else
352
		{
353
			return $string;
354
		}
355
	}
356
357
	/**
358
	 * Check if a string is json encoded
359
	 *
360
	 * @param  string $string string to check
361
	 * @return bool
362
	 */
363
	public function is_json($string)
364
	{
365
		json_decode($string);
366
		return json_last_error() === JSON_ERROR_NONE;
367
	}
368
369
	/**
370
	 * Check if a string is a valid XML
371
	 *
372
	 * @param  string $string string to check
373
	 * @return bool
374
	 */
375
	public function is_xml($string)
376
	{
377
		$internal_errors = libxml_use_internal_errors();
378
		libxml_use_internal_errors(true);
379
		$result = simplexml_load_string($string) !== false;
380
		libxml_use_internal_errors($internal_errors);
381
382
		return $result;
383
	}
384
385
	/**
386
	 * Check if a string is serialized
387
	 *
388
	 * @param  string $string string to check
389
	 * @return bool
390
	 */
391
	public function is_serialized($string)
392
	{
393
		$array = @unserialize($string);
394
		return ! ($array === false and $string !== 'b:0;');
395
	}
396
397
	/**
398
	 * Check if a string is html
399
	 *
400
	 * @param  string $string string to check
401
	 * @return bool
402
	 */
403
	public function is_html($string)
404
	{
405
		return strlen(strip_tags($string)) < strlen($string);
406
	}
407
}
408