Yoshi2889 /
SMF2.1
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * The xmlArray class is an xml parser. |
||
| 5 | * |
||
| 6 | * Simple Machines Forum (SMF) |
||
| 7 | * |
||
| 8 | * @package SMF |
||
| 9 | * @author Simple Machines http://www.simplemachines.org |
||
| 10 | * @copyright 2017 Simple Machines and individual contributors |
||
| 11 | * @license http://www.simplemachines.org/about/smf/license.php BSD |
||
| 12 | * |
||
| 13 | * @version 2.1 Beta 4 |
||
| 14 | */ |
||
| 15 | |||
| 16 | if (!defined('SMF')) |
||
| 17 | die('No direct access...'); |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Class xmlArray |
||
| 21 | * Represents an XML array |
||
| 22 | */ |
||
| 23 | class xmlArray |
||
| 24 | { |
||
| 25 | /** |
||
| 26 | * @var array Holds parsed XML results |
||
| 27 | */ |
||
| 28 | public $array; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * @var int The debugging level |
||
| 32 | */ |
||
| 33 | public $debug_level; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * holds trim level textual data |
||
| 37 | * @var bool Holds trim level textual data |
||
| 38 | */ |
||
| 39 | public $trim; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Constructor for the xml parser. |
||
| 43 | * Example use: |
||
| 44 | * $xml = new xmlArray(file('data.xml')); |
||
| 45 | * |
||
| 46 | * @param string $data The xml data or an array of, unless is_clone is true. |
||
| 47 | * @param bool $auto_trim Used to automatically trim textual data. |
||
| 48 | * @param int $level The debug level. Specifies whether notices should be generated for missing elements and attributes. |
||
| 49 | * @param bool $is_clone default false. If is_clone is true, the xmlArray is cloned from another - used internally only. |
||
| 50 | */ |
||
| 51 | public function __construct($data, $auto_trim = false, $level = null, $is_clone = false) |
||
| 52 | { |
||
| 53 | // If we're using this try to get some more memory. |
||
| 54 | setMemoryLimit('32M'); |
||
| 55 | |||
| 56 | // Set the debug level. |
||
| 57 | $this->debug_level = $level !== null ? $level : error_reporting(); |
||
| 58 | $this->trim = $auto_trim; |
||
| 59 | |||
| 60 | // Is the data already parsed? |
||
| 61 | if ($is_clone) |
||
| 62 | { |
||
| 63 | $this->array = $data; |
||
|
0 ignored issues
–
show
|
|||
| 64 | return; |
||
| 65 | } |
||
| 66 | |||
| 67 | // Is the input an array? (ie. passed from file()?) |
||
| 68 | if (is_array($data)) |
||
| 69 | $data = implode('', $data); |
||
| 70 | |||
| 71 | // Remove any xml declaration or doctype, and parse out comments and CDATA. |
||
| 72 | $data = preg_replace('/<!--.*?-->/s', '', $this->_to_cdata(preg_replace(array('/^<\?xml.+?\?' . '>/is', '/<!DOCTYPE[^>]+?' . '>/s'), '', $data))); |
||
| 73 | |||
| 74 | // Now parse the xml! |
||
| 75 | $this->array = $this->_parse($data); |
||
| 76 | } |
||
| 77 | |||
| 78 | /** |
||
| 79 | * Get the root element's name. |
||
| 80 | * Example use: |
||
| 81 | * echo $element->name(); |
||
| 82 | * @return string The root element's name |
||
| 83 | */ |
||
| 84 | public function name() |
||
| 85 | { |
||
| 86 | return isset($this->array['name']) ? $this->array['name'] : ''; |
||
| 87 | } |
||
| 88 | |||
| 89 | /** |
||
| 90 | * Get a specified element's value or attribute by path. |
||
| 91 | * Children are parsed for text, but only textual data is returned |
||
| 92 | * unless get_elements is true. |
||
| 93 | * Example use: |
||
| 94 | * $data = $xml->fetch('html/head/title'); |
||
| 95 | * @param string $path The path to the element to fetch |
||
| 96 | * @param bool $get_elements Whether to include elements |
||
| 97 | * @return string The value or attribute of the specified element |
||
| 98 | */ |
||
| 99 | public function fetch($path, $get_elements = false) |
||
| 100 | { |
||
| 101 | // Get the element, in array form. |
||
| 102 | $array = $this->path($path); |
||
| 103 | |||
| 104 | if ($array === false) |
||
| 105 | return false; |
||
| 106 | |||
| 107 | // Getting elements into this is a bit complicated... |
||
| 108 | if ($get_elements && !is_string($array)) |
||
| 109 | { |
||
| 110 | $temp = ''; |
||
| 111 | |||
| 112 | // Use the _xml() function to get the xml data. |
||
| 113 | foreach ($array->array as $val) |
||
| 114 | { |
||
| 115 | // Skip the name and any attributes. |
||
| 116 | if (is_array($val)) |
||
| 117 | $temp .= $this->_xml($val, null); |
||
| 118 | } |
||
| 119 | |||
| 120 | // Just get the XML data and then take out the CDATAs. |
||
| 121 | return $this->_to_cdata($temp); |
||
| 122 | } |
||
| 123 | |||
| 124 | // Return the value - taking care to pick out all the text values. |
||
| 125 | return is_string($array) ? $array : $this->_fetch($array->array); |
||
| 126 | } |
||
| 127 | |||
| 128 | /** Get an element, returns a new xmlArray. |
||
| 129 | * It finds any elements that match the path specified. |
||
| 130 | * It will always return a set if there is more than one of the element |
||
| 131 | * or return_set is true. |
||
| 132 | * Example use: |
||
| 133 | * $element = $xml->path('html/body'); |
||
| 134 | * @param $path string The path to the element to get |
||
| 135 | * @param $return_full bool Whether to return the full result set |
||
| 136 | * @return xmlArray, a new xmlArray. |
||
| 137 | */ |
||
| 138 | public function path($path, $return_full = false) |
||
| 139 | { |
||
| 140 | // Split up the path. |
||
| 141 | $path = explode('/', $path); |
||
| 142 | |||
| 143 | // Start with a base array. |
||
| 144 | $array = $this->array; |
||
| 145 | |||
| 146 | // For each element in the path. |
||
| 147 | foreach ($path as $el) |
||
| 148 | { |
||
| 149 | // Deal with sets.... |
||
| 150 | if (strpos($el, '[') !== false) |
||
| 151 | { |
||
| 152 | $lvl = (int) substr($el, strpos($el, '[') + 1); |
||
| 153 | $el = substr($el, 0, strpos($el, '[')); |
||
| 154 | } |
||
| 155 | // Find an attribute. |
||
| 156 | elseif (substr($el, 0, 1) == '@') |
||
| 157 | { |
||
| 158 | // It simplifies things if the attribute is already there ;). |
||
| 159 | if (isset($array[$el])) |
||
| 160 | return $array[$el]; |
||
| 161 | else |
||
| 162 | { |
||
| 163 | $trace = debug_backtrace(); |
||
| 164 | $i = 0; |
||
| 165 | View Code Duplication | while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this)) |
|
| 166 | $i++; |
||
| 167 | $debug = ' (from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line'] . ')'; |
||
| 168 | |||
| 169 | // Cause an error. |
||
| 170 | if ($this->debug_level & E_NOTICE) |
||
| 171 | trigger_error('Undefined XML attribute: ' . substr($el, 1) . $debug, E_USER_NOTICE); |
||
| 172 | return false; |
||
| 173 | } |
||
| 174 | } |
||
| 175 | else |
||
| 176 | $lvl = null; |
||
| 177 | |||
| 178 | // Find this element. |
||
| 179 | $array = $this->_path($array, $el, $lvl); |
||
| 180 | } |
||
| 181 | |||
| 182 | // Clean up after $lvl, for $return_full. |
||
| 183 | if ($return_full && (!isset($array['name']) || substr($array['name'], -1) != ']')) |
||
| 184 | $array = array('name' => $el . '[]', $array); |
||
|
0 ignored issues
–
show
The variable
$el does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
|||
| 185 | |||
| 186 | // Create the right type of class... |
||
| 187 | $newClass = get_class($this); |
||
| 188 | |||
| 189 | // Return a new xmlArray for the result. |
||
| 190 | return $array === false ? false : new $newClass($array, $this->trim, $this->debug_level, true); |
||
| 191 | } |
||
| 192 | |||
| 193 | /** |
||
| 194 | * Check if an element exists. |
||
| 195 | * Example use, |
||
| 196 | * echo $xml->exists('html/body') ? 'y' : 'n'; |
||
| 197 | * |
||
| 198 | * @param string $path The path to the element to get. |
||
| 199 | * @return boolean Whether the specified path exists |
||
| 200 | */ |
||
| 201 | public function exists($path) |
||
| 202 | { |
||
| 203 | // Split up the path. |
||
| 204 | $path = explode('/', $path); |
||
| 205 | |||
| 206 | // Start with a base array. |
||
| 207 | $array = $this->array; |
||
| 208 | |||
| 209 | // For each element in the path. |
||
| 210 | foreach ($path as $el) |
||
| 211 | { |
||
| 212 | // Deal with sets.... |
||
| 213 | if (strpos($el, '[') !== false) |
||
| 214 | { |
||
| 215 | $lvl = (int) substr($el, strpos($el, '[') + 1); |
||
| 216 | $el = substr($el, 0, strpos($el, '[')); |
||
| 217 | } |
||
| 218 | // Find an attribute. |
||
| 219 | elseif (substr($el, 0, 1) == '@') |
||
| 220 | return isset($array[$el]); |
||
| 221 | else |
||
| 222 | $lvl = null; |
||
| 223 | |||
| 224 | // Find this element. |
||
| 225 | $array = $this->_path($array, $el, $lvl, true); |
||
| 226 | } |
||
| 227 | |||
| 228 | return $array !== false; |
||
| 229 | } |
||
| 230 | |||
| 231 | /** |
||
| 232 | * Count the number of occurrences of a path. |
||
| 233 | * Example use: |
||
| 234 | * echo $xml->count('html/head/meta'); |
||
| 235 | * @param string $path The path to search for. |
||
| 236 | * @return int The number of elements the path matches. |
||
| 237 | */ |
||
| 238 | public function count($path) |
||
| 239 | { |
||
| 240 | // Get the element, always returning a full set. |
||
| 241 | $temp = $this->path($path, true); |
||
| 242 | |||
| 243 | // Start at zero, then count up all the numeric keys. |
||
| 244 | $i = 0; |
||
| 245 | foreach ($temp->array as $item) |
||
| 246 | { |
||
| 247 | if (is_array($item)) |
||
| 248 | $i++; |
||
| 249 | } |
||
| 250 | |||
| 251 | return $i; |
||
| 252 | } |
||
| 253 | |||
| 254 | /** |
||
| 255 | * Get an array of xmlArray's matching the specified path. |
||
| 256 | * This differs from ->path(path, true) in that instead of an xmlArray |
||
| 257 | * of elements, an array of xmlArray's is returned for use with foreach. |
||
| 258 | * Example use: |
||
| 259 | * foreach ($xml->set('html/body/p') as $p) |
||
| 260 | * @param $path string The path to search for. |
||
| 261 | * @return xmlArray[] An array of xmlArray objects |
||
| 262 | */ |
||
| 263 | public function set($path) |
||
| 264 | { |
||
| 265 | // None as yet, just get the path. |
||
| 266 | $array = array(); |
||
| 267 | $xml = $this->path($path, true); |
||
| 268 | |||
| 269 | foreach ($xml->array as $val) |
||
| 270 | { |
||
| 271 | // Skip these, they aren't elements. |
||
| 272 | if (!is_array($val) || $val['name'] == '!') |
||
| 273 | continue; |
||
| 274 | |||
| 275 | // Create the right type of class... |
||
| 276 | $newClass = get_class($this); |
||
| 277 | |||
| 278 | // Create a new xmlArray and stick it in the array. |
||
| 279 | $array[] = new $newClass($val, $this->trim, $this->debug_level, true); |
||
| 280 | } |
||
| 281 | |||
| 282 | return $array; |
||
| 283 | } |
||
| 284 | |||
| 285 | /** |
||
| 286 | * Create an xml file from an xmlArray, the specified path if any. |
||
| 287 | * Example use: |
||
| 288 | * echo $this->create_xml(); |
||
| 289 | * @param string $path The path to the element. (optional) |
||
| 290 | * @return string Xml-formatted string. |
||
| 291 | */ |
||
| 292 | public function create_xml($path = null) |
||
| 293 | { |
||
| 294 | // Was a path specified? If so, use that array. |
||
| 295 | View Code Duplication | if ($path !== null) |
|
| 296 | { |
||
| 297 | $path = $this->path($path); |
||
| 298 | |||
| 299 | // The path was not found |
||
| 300 | if ($path === false) |
||
| 301 | return false; |
||
| 302 | |||
| 303 | $path = $path->array; |
||
| 304 | } |
||
| 305 | // Just use the current array. |
||
| 306 | else |
||
| 307 | $path = $this->array; |
||
| 308 | |||
| 309 | // Add the xml declaration to the front. |
||
| 310 | return '<?xml version="1.0"?' . '>' . $this->_xml($path, 0); |
||
| 311 | } |
||
| 312 | |||
| 313 | /** |
||
| 314 | * Output the xml in an array form. |
||
| 315 | * Example use: |
||
| 316 | * print_r($xml->to_array()); |
||
| 317 | * |
||
| 318 | * @param string $path The path to output. |
||
| 319 | * @return array An array of XML data |
||
| 320 | */ |
||
| 321 | public function to_array($path = null) |
||
| 322 | { |
||
| 323 | // Are we doing a specific path? |
||
| 324 | View Code Duplication | if ($path !== null) |
|
| 325 | { |
||
| 326 | $path = $this->path($path); |
||
| 327 | |||
| 328 | // The path was not found |
||
| 329 | if ($path === false) |
||
| 330 | return false; |
||
| 331 | |||
| 332 | $path = $path->array; |
||
| 333 | } |
||
| 334 | // No, so just use the current array. |
||
| 335 | else |
||
| 336 | $path = $this->array; |
||
| 337 | |||
| 338 | return $this->_array($path); |
||
| 339 | } |
||
| 340 | |||
| 341 | /** |
||
| 342 | * Parse data into an array. (privately used...) |
||
| 343 | * |
||
| 344 | * @param string $data The data to parse |
||
| 345 | * @return array The parsed array |
||
| 346 | */ |
||
| 347 | protected function _parse($data) |
||
| 348 | { |
||
| 349 | // Start with an 'empty' array with no data. |
||
| 350 | $current = array( |
||
| 351 | ); |
||
| 352 | |||
| 353 | // Loop until we're out of data. |
||
| 354 | while ($data != '') |
||
| 355 | { |
||
| 356 | // Find and remove the next tag. |
||
| 357 | preg_match('/\A<([\w\-:]+)((?:\s+.+?)?)([\s]?\/)?' . '>/', $data, $match); |
||
| 358 | View Code Duplication | if (isset($match[0])) |
|
| 359 | $data = preg_replace('/' . preg_quote($match[0], '/') . '/s', '', $data, 1); |
||
| 360 | |||
| 361 | // Didn't find a tag? Keep looping.... |
||
| 362 | if (!isset($match[1]) || $match[1] == '') |
||
| 363 | { |
||
| 364 | // If there's no <, the rest is data. |
||
| 365 | if (strpos($data, '<') === false) |
||
| 366 | { |
||
| 367 | $text_value = $this->_from_cdata($data); |
||
| 368 | $data = ''; |
||
| 369 | |||
| 370 | if ($text_value != '') |
||
| 371 | $current[] = array( |
||
| 372 | 'name' => '!', |
||
| 373 | 'value' => $text_value |
||
| 374 | ); |
||
| 375 | } |
||
| 376 | // If the < isn't immediately next to the current position... more data. |
||
| 377 | elseif (strpos($data, '<') > 0) |
||
| 378 | { |
||
| 379 | $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<'))); |
||
| 380 | $data = substr($data, strpos($data, '<')); |
||
| 381 | |||
| 382 | if ($text_value != '') |
||
| 383 | $current[] = array( |
||
| 384 | 'name' => '!', |
||
| 385 | 'value' => $text_value |
||
| 386 | ); |
||
| 387 | } |
||
| 388 | // If we're looking at a </something> with no start, kill it. |
||
| 389 | elseif (strpos($data, '<') !== false && strpos($data, '<') == 0) |
||
| 390 | { |
||
| 391 | if (strpos($data, '<', 1) !== false) |
||
| 392 | { |
||
| 393 | $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<', 1))); |
||
| 394 | $data = substr($data, strpos($data, '<', 1)); |
||
| 395 | |||
| 396 | if ($text_value != '') |
||
| 397 | $current[] = array( |
||
| 398 | 'name' => '!', |
||
| 399 | 'value' => $text_value |
||
| 400 | ); |
||
| 401 | } |
||
| 402 | View Code Duplication | else |
|
| 403 | { |
||
| 404 | $text_value = $this->_from_cdata($data); |
||
| 405 | $data = ''; |
||
| 406 | |||
| 407 | if ($text_value != '') |
||
| 408 | $current[] = array( |
||
| 409 | 'name' => '!', |
||
| 410 | 'value' => $text_value |
||
| 411 | ); |
||
| 412 | } |
||
| 413 | } |
||
| 414 | |||
| 415 | // Wait for an actual occurance of an element. |
||
| 416 | continue; |
||
| 417 | } |
||
| 418 | |||
| 419 | // Create a new element in the array. |
||
| 420 | $el = &$current[]; |
||
| 421 | $el['name'] = $match[1]; |
||
| 422 | |||
| 423 | // If this ISN'T empty, remove the close tag and parse the inner data. |
||
| 424 | if ((!isset($match[3]) || trim($match[3]) != '/') && (!isset($match[2]) || trim($match[2]) != '/')) |
||
| 425 | { |
||
| 426 | // Because PHP 5.2.0+ seems to croak using regex, we'll have to do this the less fun way. |
||
| 427 | $last_tag_end = strpos($data, '</' . $match[1] . '>'); |
||
| 428 | if ($last_tag_end === false) |
||
| 429 | continue; |
||
| 430 | |||
| 431 | $offset = 0; |
||
| 432 | while (1 == 1) |
||
| 433 | { |
||
| 434 | // Where is the next start tag? |
||
| 435 | $next_tag_start = strpos($data, '<' . $match[1], $offset); |
||
| 436 | // If the next start tag is after the last end tag then we've found the right close. |
||
| 437 | if ($next_tag_start === false || $next_tag_start > $last_tag_end) |
||
| 438 | break; |
||
| 439 | |||
| 440 | // If not then find the next ending tag. |
||
| 441 | $next_tag_end = strpos($data, '</' . $match[1] . '>', $offset); |
||
| 442 | |||
| 443 | // Didn't find one? Then just use the last and sod it. |
||
| 444 | if ($next_tag_end === false) |
||
| 445 | break; |
||
| 446 | else |
||
| 447 | { |
||
| 448 | $last_tag_end = $next_tag_end; |
||
| 449 | $offset = $next_tag_start + 1; |
||
| 450 | } |
||
| 451 | } |
||
| 452 | // Parse the insides. |
||
| 453 | $inner_match = substr($data, 0, $last_tag_end); |
||
| 454 | // Data now starts from where this section ends. |
||
| 455 | $data = substr($data, $last_tag_end + strlen('</' . $match[1] . '>')); |
||
| 456 | |||
| 457 | if (!empty($inner_match)) |
||
| 458 | { |
||
| 459 | // Parse the inner data. |
||
| 460 | if (strpos($inner_match, '<') !== false) |
||
| 461 | $el += $this->_parse($inner_match); |
||
| 462 | View Code Duplication | elseif (trim($inner_match) != '') |
|
| 463 | { |
||
| 464 | $text_value = $this->_from_cdata($inner_match); |
||
| 465 | if ($text_value != '') |
||
| 466 | $el[] = array( |
||
| 467 | 'name' => '!', |
||
| 468 | 'value' => $text_value |
||
| 469 | ); |
||
| 470 | } |
||
| 471 | } |
||
| 472 | } |
||
| 473 | |||
| 474 | // If we're dealing with attributes as well, parse them out. |
||
| 475 | if (isset($match[2]) && $match[2] != '') |
||
| 476 | { |
||
| 477 | // Find all the attribute pairs in the string. |
||
| 478 | preg_match_all('/([\w:]+)="(.+?)"/', $match[2], $attr, PREG_SET_ORDER); |
||
| 479 | |||
| 480 | // Set them as @attribute-name. |
||
| 481 | foreach ($attr as $match_attr) |
||
| 482 | $el['@' . $match_attr[1]] = $match_attr[2]; |
||
| 483 | } |
||
| 484 | } |
||
| 485 | |||
| 486 | // Return the parsed array. |
||
| 487 | return $current; |
||
| 488 | } |
||
| 489 | |||
| 490 | /** |
||
| 491 | * Get a specific element's xml. (privately used...) |
||
| 492 | * |
||
| 493 | * @param array $array An array of element data |
||
| 494 | * @param null|int $indent How many levels to indent the elements (null = no indent) |
||
| 495 | * @return string The formatted XML |
||
| 496 | */ |
||
| 497 | protected function _xml($array, $indent) |
||
| 498 | { |
||
| 499 | $indentation = $indent !== null ? ' |
||
| 500 | ' . str_repeat(' ', $indent) : ''; |
||
| 501 | |||
| 502 | // This is a set of elements, with no name... |
||
| 503 | if (is_array($array) && !isset($array['name'])) |
||
| 504 | { |
||
| 505 | $temp = ''; |
||
| 506 | foreach ($array as $val) |
||
| 507 | $temp .= $this->_xml($val, $indent); |
||
| 508 | return $temp; |
||
| 509 | } |
||
| 510 | |||
| 511 | // This is just text! |
||
| 512 | if ($array['name'] == '!') |
||
| 513 | return $indentation . '<![CDATA[' . $array['value'] . ']]>'; |
||
| 514 | elseif (substr($array['name'], -2) == '[]') |
||
| 515 | $array['name'] = substr($array['name'], 0, -2); |
||
| 516 | |||
| 517 | // Start the element. |
||
| 518 | $output = $indentation . '<' . $array['name']; |
||
| 519 | |||
| 520 | $inside_elements = false; |
||
| 521 | $output_el = ''; |
||
| 522 | |||
| 523 | // Run through and recursively output all the elements or attrbutes inside this. |
||
| 524 | foreach ($array as $k => $v) |
||
| 525 | { |
||
| 526 | if (substr($k, 0, 1) == '@') |
||
| 527 | $output .= ' ' . substr($k, 1) . '="' . $v . '"'; |
||
| 528 | elseif (is_array($v)) |
||
| 529 | { |
||
| 530 | $output_el .= $this->_xml($v, $indent === null ? null : $indent + 1); |
||
| 531 | $inside_elements = true; |
||
| 532 | } |
||
| 533 | } |
||
| 534 | |||
| 535 | // Indent, if necessary.... then close the tag. |
||
| 536 | if ($inside_elements) |
||
| 537 | $output .= '>' . $output_el . $indentation . '</' . $array['name'] . '>'; |
||
| 538 | else |
||
| 539 | $output .= ' />'; |
||
| 540 | |||
| 541 | return $output; |
||
| 542 | } |
||
| 543 | |||
| 544 | /** |
||
| 545 | * Return an element as an array |
||
| 546 | * |
||
| 547 | * @param array $array An array of data |
||
| 548 | * @return string|array A string with the element's value or an array of element data |
||
| 549 | */ |
||
| 550 | protected function _array($array) |
||
| 551 | { |
||
| 552 | $return = array(); |
||
| 553 | $text = ''; |
||
| 554 | foreach ($array as $value) |
||
| 555 | { |
||
| 556 | if (!is_array($value) || !isset($value['name'])) |
||
| 557 | continue; |
||
| 558 | |||
| 559 | if ($value['name'] == '!') |
||
| 560 | $text .= $value['value']; |
||
| 561 | else |
||
| 562 | $return[$value['name']] = $this->_array($value); |
||
| 563 | } |
||
| 564 | |||
| 565 | if (empty($return)) |
||
| 566 | return $text; |
||
| 567 | else |
||
| 568 | return $return; |
||
| 569 | } |
||
| 570 | |||
| 571 | /** |
||
| 572 | * Parse out CDATA tags. (htmlspecialchars them...) |
||
| 573 | * |
||
| 574 | * @param string $data The data with CDATA tags included |
||
| 575 | * @return string The data contained within CDATA tags |
||
| 576 | */ |
||
| 577 | function _to_cdata($data) |
||
| 578 | { |
||
| 579 | $inCdata = $inComment = false; |
||
| 580 | $output = ''; |
||
| 581 | |||
| 582 | $parts = preg_split('~(<!\[CDATA\[|\]\]>|<!--|-->)~', $data, -1, PREG_SPLIT_DELIM_CAPTURE); |
||
| 583 | foreach ($parts as $part) |
||
| 584 | { |
||
| 585 | // Handle XML comments. |
||
| 586 | if (!$inCdata && $part === '<!--') |
||
| 587 | $inComment = true; |
||
| 588 | if ($inComment && $part === '-->') |
||
| 589 | $inComment = false; |
||
| 590 | elseif ($inComment) |
||
| 591 | continue; |
||
| 592 | |||
| 593 | // Handle Cdata blocks. |
||
| 594 | elseif (!$inComment && $part === '<![CDATA[') |
||
| 595 | $inCdata = true; |
||
| 596 | elseif ($inCdata && $part === ']]>') |
||
| 597 | $inCdata = false; |
||
| 598 | elseif ($inCdata) |
||
| 599 | $output .= htmlentities($part, ENT_QUOTES); |
||
| 600 | |||
| 601 | // Everything else is kept as is. |
||
| 602 | else |
||
| 603 | $output .= $part; |
||
| 604 | } |
||
| 605 | |||
| 606 | return $output; |
||
| 607 | } |
||
| 608 | |||
| 609 | /** |
||
| 610 | * Turn the CDATAs back to normal text. |
||
| 611 | * |
||
| 612 | * @param string $data The data with CDATA tags |
||
| 613 | * @return string The transformed data |
||
| 614 | */ |
||
| 615 | protected function _from_cdata($data) |
||
| 616 | { |
||
| 617 | // Get the HTML translation table and reverse it. |
||
| 618 | $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES)); |
||
| 619 | |||
| 620 | // Translate all the entities out. |
||
| 621 | $data = strtr(preg_replace_callback('~&#(\d{1,4});~', function($m) |
||
| 622 | { |
||
| 623 | return chr("$m[1]"); |
||
| 624 | }, $data), $trans_tbl); |
||
| 625 | |||
| 626 | return $this->trim ? trim($data) : $data; |
||
| 627 | } |
||
| 628 | |||
| 629 | /** |
||
| 630 | * Given an array, return the text from that array. (recursive and privately used.) |
||
| 631 | * |
||
| 632 | * @param array $array An aray of data |
||
| 633 | * @return string The text from the array |
||
| 634 | */ |
||
| 635 | protected function _fetch($array) |
||
| 636 | { |
||
| 637 | // Don't return anything if this is just a string. |
||
| 638 | if (is_string($array)) |
||
| 639 | return ''; |
||
| 640 | |||
| 641 | $temp = ''; |
||
| 642 | foreach ($array as $text) |
||
| 643 | { |
||
| 644 | // This means it's most likely an attribute or the name itself. |
||
| 645 | if (!isset($text['name'])) |
||
| 646 | continue; |
||
| 647 | |||
| 648 | // This is text! |
||
| 649 | if ($text['name'] == '!') |
||
| 650 | $temp .= $text['value']; |
||
| 651 | // Another element - dive in ;). |
||
| 652 | else |
||
| 653 | $temp .= $this->_fetch($text); |
||
| 654 | } |
||
| 655 | |||
| 656 | // Return all the bits and pieces we've put together. |
||
| 657 | return $temp; |
||
| 658 | } |
||
| 659 | |||
| 660 | /** |
||
| 661 | * Get a specific array by path, one level down. (privately used...) |
||
| 662 | * |
||
| 663 | * @param array $array An array of data |
||
| 664 | * @param string $path The path |
||
| 665 | * @param int $level How far deep into the array we should go |
||
| 666 | * @param bool $no_error Whether or not to ignore errors |
||
| 667 | * @return string|array The specified array (or the contents of said array if there's only one result) |
||
| 668 | */ |
||
| 669 | protected function _path($array, $path, $level, $no_error = false) |
||
| 670 | { |
||
| 671 | // Is $array even an array? It might be false! |
||
| 672 | if (!is_array($array)) |
||
| 673 | return false; |
||
| 674 | |||
| 675 | // Asking for *no* path? |
||
| 676 | if ($path == '' || $path == '.') |
||
| 677 | return $array; |
||
| 678 | $paths = explode('|', $path); |
||
| 679 | |||
| 680 | // A * means all elements of any name. |
||
| 681 | $show_all = in_array('*', $paths); |
||
| 682 | |||
| 683 | $results = array(); |
||
| 684 | |||
| 685 | // Check each element. |
||
| 686 | foreach ($array as $value) |
||
| 687 | { |
||
| 688 | if (!is_array($value) || $value['name'] === '!') |
||
| 689 | continue; |
||
| 690 | |||
| 691 | if ($show_all || in_array($value['name'], $paths)) |
||
| 692 | { |
||
| 693 | // Skip elements before "the one". |
||
| 694 | if ($level !== null && $level > 0) |
||
| 695 | $level--; |
||
| 696 | else |
||
| 697 | $results[] = $value; |
||
| 698 | } |
||
| 699 | } |
||
| 700 | |||
| 701 | // No results found... |
||
| 702 | if (empty($results)) |
||
| 703 | { |
||
| 704 | $trace = debug_backtrace(); |
||
| 705 | $i = 0; |
||
| 706 | View Code Duplication | while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this)) |
|
| 707 | $i++; |
||
| 708 | $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line']; |
||
| 709 | |||
| 710 | // Cause an error. |
||
| 711 | if ($this->debug_level & E_NOTICE && !$no_error) |
||
| 712 | trigger_error('Undefined XML element: ' . $path . $debug, E_USER_NOTICE); |
||
| 713 | return false; |
||
| 714 | } |
||
| 715 | // Only one result. |
||
| 716 | elseif (count($results) == 1 || $level !== null) |
||
| 717 | return $results[0]; |
||
| 718 | // Return the result set. |
||
| 719 | else |
||
| 720 | return $results + array('name' => $path . '[]'); |
||
| 721 | } |
||
| 722 | } |
||
| 723 | |||
| 724 | /** |
||
| 725 | * Class ftp_connection |
||
| 726 | * Simple FTP protocol implementation. |
||
| 727 | * |
||
| 728 | * @see https://tools.ietf.org/html/rfc959 |
||
| 729 | */ |
||
| 730 | class ftp_connection |
||
| 731 | { |
||
| 732 | /** |
||
| 733 | * @var string Holds the connection response |
||
| 734 | */ |
||
| 735 | public $connection; |
||
| 736 | |||
| 737 | /** |
||
| 738 | * @var string Holds any errors |
||
| 739 | */ |
||
| 740 | public $error; |
||
| 741 | |||
| 742 | /** |
||
| 743 | * @var string Holds the last message from the server |
||
| 744 | */ |
||
| 745 | public $last_message; |
||
| 746 | |||
| 747 | /** |
||
| 748 | * @var boolean Whether or not this is a passive connection |
||
| 749 | */ |
||
| 750 | public $pasv; |
||
| 751 | |||
| 752 | /** |
||
| 753 | * Create a new FTP connection... |
||
| 754 | * |
||
| 755 | * @param string $ftp_server The server to connect to |
||
| 756 | * @param int $ftp_port The port to connect to |
||
| 757 | * @param string $ftp_user The username |
||
| 758 | * @param string $ftp_pass The password |
||
| 759 | */ |
||
| 760 | public function __construct($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = '[email protected]') |
||
| 761 | { |
||
| 762 | // Initialize variables. |
||
| 763 | $this->connection = 'no_connection'; |
||
| 764 | $this->error = false; |
||
| 765 | $this->pasv = array(); |
||
|
0 ignored issues
–
show
It seems like
array() of type array is incompatible with the declared type boolean of property $pasv.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 766 | |||
| 767 | if ($ftp_server !== null) |
||
| 768 | $this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass); |
||
| 769 | } |
||
| 770 | |||
| 771 | /** |
||
| 772 | * Connects to a server |
||
| 773 | * |
||
| 774 | * @param string $ftp_server The address of the server |
||
| 775 | * @param int $ftp_port The port |
||
| 776 | * @param string $ftp_user The username |
||
| 777 | * @param string $ftp_pass The password |
||
| 778 | */ |
||
| 779 | public function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = '[email protected]') |
||
| 780 | { |
||
| 781 | View Code Duplication | if (strpos($ftp_server, 'ftp://') === 0) |
|
| 782 | $ftp_server = substr($ftp_server, 6); |
||
| 783 | elseif (strpos($ftp_server, 'ftps://') === 0) |
||
| 784 | $ftp_server = 'ssl://' . substr($ftp_server, 7); |
||
| 785 | View Code Duplication | if (strpos($ftp_server, 'http://') === 0) |
|
| 786 | $ftp_server = substr($ftp_server, 7); |
||
| 787 | elseif (strpos($ftp_server, 'https://') === 0) |
||
| 788 | $ftp_server = substr($ftp_server, 8); |
||
| 789 | $ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => '')); |
||
| 790 | |||
| 791 | // Connect to the FTP server. |
||
| 792 | $this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5); |
||
| 793 | if (!$this->connection) |
||
| 794 | { |
||
| 795 | $this->error = 'bad_server'; |
||
| 796 | $this->last_message = 'Invalid Server'; |
||
| 797 | return; |
||
| 798 | } |
||
| 799 | |||
| 800 | // Get the welcome message... |
||
| 801 | if (!$this->check_response(220)) |
||
| 802 | { |
||
| 803 | $this->error = 'bad_response'; |
||
| 804 | $this->last_message = 'Bad Response'; |
||
| 805 | return; |
||
| 806 | } |
||
| 807 | |||
| 808 | // Send the username, it should ask for a password. |
||
| 809 | fwrite($this->connection, 'USER ' . $ftp_user . "\r\n"); |
||
| 810 | |||
| 811 | if (!$this->check_response(331)) |
||
| 812 | { |
||
| 813 | $this->error = 'bad_username'; |
||
| 814 | $this->last_message = 'Invalid Username'; |
||
| 815 | return; |
||
| 816 | } |
||
| 817 | |||
| 818 | // Now send the password... and hope it goes okay. |
||
| 819 | |||
| 820 | fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n"); |
||
| 821 | if (!$this->check_response(230)) |
||
| 822 | { |
||
| 823 | $this->error = 'bad_password'; |
||
| 824 | $this->last_message = 'Invalid Password'; |
||
| 825 | return; |
||
| 826 | } |
||
| 827 | } |
||
| 828 | |||
| 829 | /** |
||
| 830 | * Changes to a directory (chdir) via the ftp connection |
||
| 831 | * |
||
| 832 | * @param string $ftp_path The path to the directory we want to change to |
||
| 833 | * @return boolean Whether or not the operation was successful |
||
| 834 | */ |
||
| 835 | public function chdir($ftp_path) |
||
| 836 | { |
||
| 837 | if (!is_resource($this->connection)) |
||
| 838 | return false; |
||
| 839 | |||
| 840 | // No slash on the end, please... |
||
| 841 | if ($ftp_path !== '/' && substr($ftp_path, -1) === '/') |
||
| 842 | $ftp_path = substr($ftp_path, 0, -1); |
||
| 843 | |||
| 844 | fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n"); |
||
| 845 | if (!$this->check_response(250)) |
||
| 846 | { |
||
| 847 | $this->error = 'bad_path'; |
||
| 848 | return false; |
||
| 849 | } |
||
| 850 | |||
| 851 | return true; |
||
| 852 | } |
||
| 853 | |||
| 854 | /** |
||
| 855 | * Changes a files atrributes (chmod) |
||
| 856 | * |
||
| 857 | * @param string $ftp_file The file to CHMOD |
||
| 858 | * @param int|string $chmod The value for the CHMOD operation |
||
| 859 | * @return boolean Whether or not the operation was successful |
||
| 860 | */ |
||
| 861 | public function chmod($ftp_file, $chmod) |
||
| 862 | { |
||
| 863 | if (!is_resource($this->connection)) |
||
| 864 | return false; |
||
| 865 | |||
| 866 | if ($ftp_file == '') |
||
| 867 | $ftp_file = '.'; |
||
| 868 | |||
| 869 | // Do we have a file or a dir? |
||
| 870 | $is_dir = is_dir($ftp_file); |
||
| 871 | $is_writable = false; |
||
| 872 | |||
| 873 | // Set different modes. |
||
| 874 | $chmod_values = $is_dir ? array(0750, 0755, 0775, 0777) : array(0644, 0664, 0666); |
||
| 875 | |||
| 876 | foreach ($chmod_values as $val) |
||
| 877 | { |
||
| 878 | // If it's writable, break out of the loop. |
||
| 879 | if (is_writable($ftp_file)) |
||
| 880 | { |
||
| 881 | $is_writable = true; |
||
| 882 | break; |
||
| 883 | } |
||
| 884 | |||
| 885 | else |
||
| 886 | { |
||
| 887 | // Convert the chmod value from octal (0777) to text ("777"). |
||
| 888 | fwrite($this->connection, 'SITE CHMOD ' . decoct($val) . ' ' . $ftp_file . "\r\n"); |
||
| 889 | if (!$this->check_response(200)) |
||
| 890 | { |
||
| 891 | $this->error = 'bad_file'; |
||
| 892 | break; |
||
| 893 | } |
||
| 894 | } |
||
| 895 | } |
||
| 896 | return $is_writable; |
||
| 897 | } |
||
| 898 | |||
| 899 | /** |
||
| 900 | * Deletes a file |
||
| 901 | * |
||
| 902 | * @param string $ftp_file The file to delete |
||
| 903 | * @return boolean Whether or not the operation was successful |
||
| 904 | */ |
||
| 905 | public function unlink($ftp_file) |
||
| 906 | { |
||
| 907 | // We are actually connected, right? |
||
| 908 | if (!is_resource($this->connection)) |
||
| 909 | return false; |
||
| 910 | |||
| 911 | // Delete file X. |
||
| 912 | fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n"); |
||
| 913 | if (!$this->check_response(250)) |
||
| 914 | { |
||
| 915 | fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n"); |
||
| 916 | |||
| 917 | // Still no love? |
||
| 918 | if (!$this->check_response(250)) |
||
| 919 | { |
||
| 920 | $this->error = 'bad_file'; |
||
| 921 | return false; |
||
| 922 | } |
||
| 923 | } |
||
| 924 | |||
| 925 | return true; |
||
| 926 | } |
||
| 927 | |||
| 928 | /** |
||
| 929 | * Reads the response to the command from the server |
||
| 930 | * |
||
| 931 | * @param string $desired The desired response |
||
| 932 | * @return boolean Whether or not we got the desired response |
||
| 933 | */ |
||
| 934 | public function check_response($desired) |
||
| 935 | { |
||
| 936 | // Wait for a response that isn't continued with -, but don't wait too long. |
||
| 937 | $time = time(); |
||
| 938 | do |
||
| 939 | $this->last_message = fgets($this->connection, 1024); |
||
| 940 | while ((strlen($this->last_message) < 4 || strpos($this->last_message, ' ') === 0 || strpos($this->last_message, ' ', 3) !== 3) && time() - $time < 5); |
||
| 941 | |||
| 942 | // Was the desired response returned? |
||
| 943 | return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired; |
||
| 944 | } |
||
| 945 | |||
| 946 | /** |
||
| 947 | * Used to create a passive connection |
||
| 948 | * |
||
| 949 | * @return boolean Whether the passive connection was created successfully |
||
| 950 | */ |
||
| 951 | public function passive() |
||
| 952 | { |
||
| 953 | // We can't create a passive data connection without a primary one first being there. |
||
| 954 | if (!is_resource($this->connection)) |
||
| 955 | return false; |
||
| 956 | |||
| 957 | // Request a passive connection - this means, we'll talk to you, you don't talk to us. |
||
| 958 | @fwrite($this->connection, 'PASV' . "\r\n"); |
||
|
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
| 959 | $time = time(); |
||
| 960 | do |
||
| 961 | $response = fgets($this->connection, 1024); |
||
| 962 | while (strpos($response, ' ', 3) !== 3 && time() - $time < 5); |
||
| 963 | |||
| 964 | // If it's not 227, we weren't given an IP and port, which means it failed. |
||
| 965 | if (strpos($response, '227 ') !== 0) |
||
| 966 | { |
||
| 967 | $this->error = 'bad_response'; |
||
| 968 | return false; |
||
| 969 | } |
||
| 970 | |||
| 971 | // Snatch the IP and port information, or die horribly trying... |
||
| 972 | if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0) |
||
| 973 | { |
||
| 974 | $this->error = 'bad_response'; |
||
| 975 | return false; |
||
| 976 | } |
||
| 977 | |||
| 978 | // This is pretty simple - store it for later use ;). |
||
| 979 | $this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]); |
||
|
0 ignored issues
–
show
It seems like
array('ip' => $match[1] ...h[5] * 256 + $match[6]) of type array<string,string|doub...ring","port":"double"}> is incompatible with the declared type boolean of property $pasv.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 980 | |||
| 981 | return true; |
||
| 982 | } |
||
| 983 | |||
| 984 | /** |
||
| 985 | * Creates a new file on the server |
||
| 986 | * |
||
| 987 | * @param string $ftp_file The file to create |
||
| 988 | * @return boolean Whether or not the file was created successfully |
||
| 989 | */ |
||
| 990 | public function create_file($ftp_file) |
||
| 991 | { |
||
| 992 | // First, we have to be connected... very important. |
||
| 993 | if (!is_resource($this->connection)) |
||
| 994 | return false; |
||
| 995 | |||
| 996 | // I'd like one passive mode, please! |
||
| 997 | if (!$this->passive()) |
||
| 998 | return false; |
||
| 999 | |||
| 1000 | // Seems logical enough, so far... |
||
| 1001 | fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n"); |
||
| 1002 | |||
| 1003 | // Okay, now we connect to the data port. If it doesn't work out, it's probably "file already exists", etc. |
||
| 1004 | $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); |
||
| 1005 | if (!$fp || !$this->check_response(150)) |
||
| 1006 | { |
||
| 1007 | $this->error = 'bad_file'; |
||
| 1008 | @fclose($fp); |
||
|
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
| 1009 | return false; |
||
| 1010 | } |
||
| 1011 | |||
| 1012 | // This may look strange, but we're just closing it to indicate a zero-byte upload. |
||
| 1013 | fclose($fp); |
||
| 1014 | if (!$this->check_response(226)) |
||
| 1015 | { |
||
| 1016 | $this->error = 'bad_response'; |
||
| 1017 | return false; |
||
| 1018 | } |
||
| 1019 | |||
| 1020 | return true; |
||
| 1021 | } |
||
| 1022 | |||
| 1023 | /** |
||
| 1024 | * Generates a directory listing for the current directory |
||
| 1025 | * |
||
| 1026 | * @param string $ftp_path The path to the directory |
||
| 1027 | * @param bool $search Whether or not to get a recursive directory listing |
||
| 1028 | * @return string|boolean The results of the command or false if unsuccessful |
||
| 1029 | */ |
||
| 1030 | public function list_dir($ftp_path = '', $search = false) |
||
| 1031 | { |
||
| 1032 | // Are we even connected...? |
||
| 1033 | if (!is_resource($this->connection)) |
||
| 1034 | return false; |
||
| 1035 | |||
| 1036 | // Passive... non-agressive... |
||
| 1037 | if (!$this->passive()) |
||
| 1038 | return false; |
||
| 1039 | |||
| 1040 | // Get the listing! |
||
| 1041 | fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n"); |
||
| 1042 | |||
| 1043 | // Connect, assuming we've got a connection. |
||
| 1044 | $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); |
||
| 1045 | if (!$fp || !$this->check_response(array(150, 125))) |
||
| 1046 | { |
||
| 1047 | $this->error = 'bad_response'; |
||
| 1048 | @fclose($fp); |
||
|
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
| 1049 | return false; |
||
| 1050 | } |
||
| 1051 | |||
| 1052 | // Read in the file listing. |
||
| 1053 | $data = ''; |
||
| 1054 | while (!feof($fp)) |
||
| 1055 | $data .= fread($fp, 4096); |
||
| 1056 | fclose($fp); |
||
| 1057 | |||
| 1058 | // Everything go okay? |
||
| 1059 | if (!$this->check_response(226)) |
||
| 1060 | { |
||
| 1061 | $this->error = 'bad_response'; |
||
| 1062 | return false; |
||
| 1063 | } |
||
| 1064 | |||
| 1065 | return $data; |
||
| 1066 | } |
||
| 1067 | |||
| 1068 | /** |
||
| 1069 | * Determines the current directory we are in |
||
| 1070 | * |
||
| 1071 | * @param string $file The name of a file |
||
| 1072 | * @param string $listing A directory listing or null to generate one |
||
| 1073 | * @return string|boolean The name of the file or false if it wasn't found |
||
| 1074 | */ |
||
| 1075 | public function locate($file, $listing = null) |
||
| 1076 | { |
||
| 1077 | if ($listing === null) |
||
| 1078 | $listing = $this->list_dir('', true); |
||
| 1079 | $listing = explode("\n", $listing); |
||
| 1080 | |||
| 1081 | @fwrite($this->connection, 'PWD' . "\r\n"); |
||
|
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
| 1082 | $time = time(); |
||
| 1083 | do |
||
| 1084 | $response = fgets($this->connection, 1024); |
||
| 1085 | while ($response[3] != ' ' && time() - $time < 5); |
||
| 1086 | |||
| 1087 | // Check for 257! |
||
| 1088 | if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0) |
||
| 1089 | $current_dir = strtr($match[1], array('""' => '"')); |
||
| 1090 | else |
||
| 1091 | $current_dir = ''; |
||
| 1092 | |||
| 1093 | for ($i = 0, $n = count($listing); $i < $n; $i++) |
||
| 1094 | { |
||
| 1095 | if (trim($listing[$i]) == '' && isset($listing[$i + 1])) |
||
| 1096 | { |
||
| 1097 | $current_dir = substr(trim($listing[++$i]), 0, -1); |
||
| 1098 | $i++; |
||
| 1099 | } |
||
| 1100 | |||
| 1101 | // Okay, this file's name is: |
||
| 1102 | $listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]); |
||
| 1103 | |||
| 1104 | if ($file[0] == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1)) |
||
| 1105 | return $listing[$i]; |
||
| 1106 | if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1)) |
||
| 1107 | return $listing[$i]; |
||
| 1108 | if (basename($listing[$i]) == $file || $listing[$i] == $file) |
||
| 1109 | return $listing[$i]; |
||
| 1110 | } |
||
| 1111 | |||
| 1112 | return false; |
||
| 1113 | } |
||
| 1114 | |||
| 1115 | /** |
||
| 1116 | * Creates a new directory on the server |
||
| 1117 | * |
||
| 1118 | * @param string $ftp_dir The name of the directory to create |
||
| 1119 | * @return boolean Whether or not the operation was successful |
||
| 1120 | */ |
||
| 1121 | public function create_dir($ftp_dir) |
||
| 1122 | { |
||
| 1123 | // We must be connected to the server to do something. |
||
| 1124 | if (!is_resource($this->connection)) |
||
| 1125 | return false; |
||
| 1126 | |||
| 1127 | // Make this new beautiful directory! |
||
| 1128 | fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n"); |
||
| 1129 | if (!$this->check_response(257)) |
||
| 1130 | { |
||
| 1131 | $this->error = 'bad_file'; |
||
| 1132 | return false; |
||
| 1133 | } |
||
| 1134 | |||
| 1135 | return true; |
||
| 1136 | } |
||
| 1137 | |||
| 1138 | /** |
||
| 1139 | * Detects the current path |
||
| 1140 | * |
||
| 1141 | * @param string $filesystem_path The full path from the filesystem |
||
| 1142 | * @param string $lookup_file The name of a file in the specified path |
||
| 1143 | * @return array An array of detected info - username, path from FTP root and whether or not the current path was found |
||
| 1144 | */ |
||
| 1145 | public function detect_path($filesystem_path, $lookup_file = null) |
||
| 1146 | { |
||
| 1147 | $username = ''; |
||
| 1148 | |||
| 1149 | if (isset($_SERVER['DOCUMENT_ROOT'])) |
||
| 1150 | { |
||
| 1151 | if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) |
||
| 1152 | { |
||
| 1153 | $username = $match[1]; |
||
| 1154 | |||
| 1155 | $path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => '')); |
||
| 1156 | |||
| 1157 | if (substr($path, -1) == '/') |
||
| 1158 | $path = substr($path, 0, -1); |
||
| 1159 | |||
| 1160 | if (strlen(dirname($_SERVER['PHP_SELF'])) > 1) |
||
| 1161 | $path .= dirname($_SERVER['PHP_SELF']); |
||
| 1162 | } |
||
| 1163 | elseif (strpos($filesystem_path, '/var/www/') === 0) |
||
| 1164 | $path = substr($filesystem_path, 8); |
||
| 1165 | else |
||
| 1166 | $path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => '')); |
||
| 1167 | } |
||
| 1168 | else |
||
| 1169 | $path = ''; |
||
| 1170 | |||
| 1171 | if (is_resource($this->connection) && $this->list_dir($path) == '') |
||
| 1172 | { |
||
| 1173 | $data = $this->list_dir('', true); |
||
| 1174 | |||
| 1175 | if ($lookup_file === null) |
||
| 1176 | $lookup_file = $_SERVER['PHP_SELF']; |
||
| 1177 | |||
| 1178 | $found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data)); |
||
|
0 ignored issues
–
show
It seems like
$data defined by $this->list_dir('', true) on line 1173 can also be of type false; however, ftp_connection::locate() does only seem to accept string|null, did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new Loading history...
|
|||
| 1179 | if ($found_path == false) |
||
|
0 ignored issues
–
show
|
|||
| 1180 | $found_path = dirname($this->locate(basename($lookup_file))); |
||
| 1181 | if ($found_path != false) |
||
|
0 ignored issues
–
show
|
|||
| 1182 | $path = $found_path; |
||
| 1183 | } |
||
| 1184 | elseif (is_resource($this->connection)) |
||
| 1185 | $found_path = true; |
||
| 1186 | |||
| 1187 | return array($username, $path, isset($found_path)); |
||
| 1188 | } |
||
| 1189 | |||
| 1190 | /** |
||
| 1191 | * Close the ftp connection |
||
| 1192 | * |
||
| 1193 | * @return boolean Always returns true |
||
| 1194 | */ |
||
| 1195 | public function close() |
||
| 1196 | { |
||
| 1197 | // Goodbye! |
||
| 1198 | fwrite($this->connection, 'QUIT' . "\r\n"); |
||
| 1199 | fclose($this->connection); |
||
| 1200 | |||
| 1201 | return true; |
||
| 1202 | } |
||
| 1203 | } |
||
| 1204 | |||
| 1205 | ?> |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..