Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Private url module utility functions
  20   *
  21   * @package    mod_url
  22   * @copyright  2009 Petr Skoda  {@link http://skodak.org}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die;
  27  
  28  require_once("$CFG->libdir/filelib.php");
  29  require_once("$CFG->libdir/resourcelib.php");
  30  require_once("$CFG->dirroot/mod/url/lib.php");
  31  
  32  /**
  33   * This methods does weak url validation, we are looking for major problems only,
  34   * no strict RFE validation.
  35   *
  36   * @param $url
  37   * @return bool true is seems valid, false if definitely not valid URL
  38   */
  39  function url_appears_valid_url($url) {
  40      if (preg_match('/^(\/|https?:|ftp:)/i', $url)) {
  41          // note: this is not exact validation, we look for severely malformed URLs only
  42          return (bool) preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[^ @]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
  43      } else {
  44          return (bool)preg_match('/^[a-z]+:\/\/...*$/i', $url);
  45      }
  46  }
  47  
  48  /**
  49   * Fix common URL problems that we want teachers to see fixed
  50   * the next time they edit the resource.
  51   *
  52   * This function does not include any XSS protection.
  53   *
  54   * @param string $url
  55   * @return string
  56   */
  57  function url_fix_submitted_url($url) {
  58      // note: empty urls are prevented in form validation
  59      $url = trim($url);
  60  
  61      // remove encoded entities - we want the raw URI here
  62      $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
  63  
  64      if (!preg_match('|^[a-z]+:|i', $url) and !preg_match('|^/|', $url)) {
  65          // invalid URI, try to fix it by making it normal URL,
  66          // please note relative urls are not allowed, /xx/yy links are ok
  67          $url = 'http://'.$url;
  68      }
  69  
  70      return $url;
  71  }
  72  
  73  /**
  74   * Return full url with all extra parameters
  75   *
  76   * This function does not include any XSS protection.
  77   *
  78   * @param string $url
  79   * @param object $cm
  80   * @param object $course
  81   * @param object $config
  82   * @return string url with & encoded as &amp;
  83   */
  84  function url_get_full_url($url, $cm, $course, $config=null) {
  85  
  86      $parameters = empty($url->parameters) ? [] : (array) unserialize_array($url->parameters);
  87  
  88      // make sure there are no encoded entities, it is ok to do this twice
  89      $fullurl = html_entity_decode($url->externalurl, ENT_QUOTES, 'UTF-8');
  90  
  91      $letters = '\pL';
  92      $latin = 'a-zA-Z';
  93      $digits = '0-9';
  94      $symbols = '\x{20E3}\x{00AE}\x{00A9}\x{203C}\x{2047}\x{2048}\x{2049}\x{3030}\x{303D}\x{2139}\x{2122}\x{3297}\x{3299}' .
  95                 '\x{2300}-\x{23FF}\x{2600}-\x{27BF}\x{2B00}-\x{2BF0}';
  96      $arabic = '\x{FE00}-\x{FEFF}';
  97      $math = '\x{2190}-\x{21FF}\x{2900}-\x{297F}';
  98      $othernumbers = '\x{2460}-\x{24FF}';
  99      $geometric = '\x{25A0}-\x{25FF}';
 100      $emojis = '\x{1F000}-\x{1F6FF}';
 101  
 102      if (preg_match('/^(\/|https?:|ftp:)/i', $fullurl) or preg_match('|^/|', $fullurl)) {
 103          // encode extra chars in URLs - this does not make it always valid, but it helps with some UTF-8 problems
 104          // Thanks to 💩.la emojis count as valid, too.
 105          $allowed = "[" . $letters . $latin . $digits . $symbols . $arabic . $math . $othernumbers . $geometric .
 106              $emojis . "]" . preg_quote(';/?:@=&$_.+!*(),-#%', '/');
 107          $fullurl = preg_replace_callback("/[^$allowed]/u", 'url_filter_callback', $fullurl);
 108      } else {
 109          // encode special chars only
 110          $fullurl = str_replace('"', '%22', $fullurl);
 111          $fullurl = str_replace('\'', '%27', $fullurl);
 112          $fullurl = str_replace(' ', '%20', $fullurl);
 113          $fullurl = str_replace('<', '%3C', $fullurl);
 114          $fullurl = str_replace('>', '%3E', $fullurl);
 115      }
 116  
 117      // add variable url parameters
 118      if (!empty($parameters)) {
 119          if (!$config) {
 120              $config = get_config('url');
 121          }
 122          $paramvalues = url_get_variable_values($url, $cm, $course, $config);
 123  
 124          foreach ($parameters as $parse=>$parameter) {
 125              if (isset($paramvalues[$parameter])) {
 126                  $parameters[$parse] = rawurlencode($parse).'='.rawurlencode($paramvalues[$parameter]);
 127              } else {
 128                  unset($parameters[$parse]);
 129              }
 130          }
 131  
 132          if (!empty($parameters)) {
 133              if (stripos($fullurl, 'teamspeak://') === 0) {
 134                  $fullurl = $fullurl.'?'.implode('?', $parameters);
 135              } else {
 136                  $join = (strpos($fullurl, '?') === false) ? '?' : '&';
 137                  $fullurl = $fullurl.$join.implode('&', $parameters);
 138              }
 139          }
 140      }
 141  
 142      // encode all & to &amp; entity
 143      $fullurl = str_replace('&', '&amp;', $fullurl);
 144  
 145      return $fullurl;
 146  }
 147  
 148  /**
 149   * Unicode encoding helper callback
 150   * @internal
 151   * @param array $matches
 152   * @return string
 153   */
 154  function url_filter_callback($matches) {
 155      return rawurlencode($matches[0]);
 156  }
 157  
 158  /**
 159   * Print url header.
 160   * @param object $url
 161   * @param object $cm
 162   * @param object $course
 163   * @return void
 164   */
 165  function url_print_header($url, $cm, $course) {
 166      global $PAGE, $OUTPUT;
 167  
 168      $PAGE->set_title($course->shortname.': '.$url->name);
 169      $PAGE->set_heading($course->fullname);
 170      $PAGE->set_activity_record($url);
 171      echo $OUTPUT->header();
 172  }
 173  
 174  /**
 175   * Print url heading.
 176   * @param object $url
 177   * @param object $cm
 178   * @param object $course
 179   * @param bool $notused This variable is no longer used.
 180   * @return void
 181   */
 182  function url_print_heading($url, $cm, $course, $notused = false) {
 183      global $OUTPUT;
 184      echo $OUTPUT->heading(format_string($url->name), 2);
 185  }
 186  
 187  /**
 188   * Print url introduction.
 189   * @param object $url
 190   * @param object $cm
 191   * @param object $course
 192   * @param bool $ignoresettings print even if not specified in modedit
 193   * @return void
 194   */
 195  function url_print_intro($url, $cm, $course, $ignoresettings=false) {
 196      global $OUTPUT;
 197  
 198      $options = empty($url->displayoptions) ? [] : (array) unserialize_array($url->displayoptions);
 199      if ($ignoresettings or !empty($options['printintro'])) {
 200          if (trim(strip_tags($url->intro))) {
 201              echo $OUTPUT->box_start('mod_introbox', 'urlintro');
 202              echo format_module_intro('url', $url, $cm->id);
 203              echo $OUTPUT->box_end();
 204          }
 205      }
 206  }
 207  
 208  /**
 209   * Display url frames.
 210   * @param object $url
 211   * @param object $cm
 212   * @param object $course
 213   * @return does not return
 214   */
 215  function url_display_frame($url, $cm, $course) {
 216      global $PAGE, $OUTPUT, $CFG;
 217  
 218      $frame = optional_param('frameset', 'main', PARAM_ALPHA);
 219  
 220      if ($frame === 'top') {
 221          $PAGE->set_pagelayout('frametop');
 222          url_print_header($url, $cm, $course);
 223          url_print_heading($url, $cm, $course);
 224          url_print_intro($url, $cm, $course);
 225          echo $OUTPUT->footer();
 226          die;
 227  
 228      } else {
 229          $config = get_config('url');
 230          $context = context_module::instance($cm->id);
 231          $exteurl = url_get_full_url($url, $cm, $course, $config);
 232          $navurl = "$CFG->wwwroot/mod/url/view.php?id=$cm->id&amp;frameset=top";
 233          $coursecontext = context_course::instance($course->id);
 234          $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
 235          $title = strip_tags($courseshortname.': '.format_string($url->name));
 236          $framesize = $config->framesize;
 237          $modulename = s(get_string('modulename','url'));
 238          $contentframetitle = s(format_string($url->name));
 239          $dir = get_string('thisdirection', 'langconfig');
 240  
 241          $extframe = <<<EOF
 242  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
 243  <html dir="$dir">
 244    <head>
 245      <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 246      <title>$title</title>
 247    </head>
 248    <frameset rows="$framesize,*">
 249      <frame src="$navurl" title="$modulename"/>
 250      <frame src="$exteurl" title="$contentframetitle"/>
 251    </frameset>
 252  </html>
 253  EOF;
 254  
 255          @header('Content-Type: text/html; charset=utf-8');
 256          echo $extframe;
 257          die;
 258      }
 259  }
 260  
 261  /**
 262   * Print url info and link.
 263   * @param object $url
 264   * @param object $cm
 265   * @param object $course
 266   * @return does not return
 267   */
 268  function url_print_workaround($url, $cm, $course) {
 269      global $OUTPUT, $USER;
 270  
 271      url_print_header($url, $cm, $course);
 272      url_print_heading($url, $cm, $course, true);
 273  
 274      // Display any activity information (eg completion requirements / dates).
 275      $cminfo = cm_info::create($cm);
 276      $completiondetails = \core_completion\cm_completion_details::get_instance($cminfo, $USER->id);
 277      $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id);
 278      echo $OUTPUT->activity_information($cminfo, $completiondetails, $activitydates);
 279  
 280      url_print_intro($url, $cm, $course, true);
 281  
 282      $fullurl = url_get_full_url($url, $cm, $course);
 283  
 284      $display = url_get_final_display_type($url);
 285      if ($display == RESOURCELIB_DISPLAY_POPUP) {
 286          $jsfullurl = addslashes_js($fullurl);
 287          $options = empty($url->displayoptions) ? [] : (array) unserialize_array($url->displayoptions);
 288          $width  = empty($options['popupwidth'])  ? 620 : $options['popupwidth'];
 289          $height = empty($options['popupheight']) ? 450 : $options['popupheight'];
 290          $wh = "width=$width,height=$height,toolbar=no,location=no,menubar=no,copyhistory=no,status=no,directories=no,scrollbars=yes,resizable=yes";
 291          $extra = "onclick=\"window.open('$jsfullurl', '', '$wh'); return false;\"";
 292  
 293      } else if ($display == RESOURCELIB_DISPLAY_NEW) {
 294          $extra = "onclick=\"this.target='_blank';\"";
 295  
 296      } else {
 297          $extra = '';
 298      }
 299  
 300      echo '<div class="urlworkaround">';
 301      print_string('clicktoopen', 'url', "<a href=\"$fullurl\" $extra>$fullurl</a>");
 302      echo '</div>';
 303  
 304      echo $OUTPUT->footer();
 305      die;
 306  }
 307  
 308  /**
 309   * Display embedded url file.
 310   * @param object $url
 311   * @param object $cm
 312   * @param object $course
 313   * @return does not return
 314   */
 315  function url_display_embed($url, $cm, $course) {
 316      global $PAGE, $OUTPUT, $USER;
 317  
 318      $mimetype = resourcelib_guess_url_mimetype($url->externalurl);
 319      $fullurl  = url_get_full_url($url, $cm, $course);
 320      $title    = $url->name;
 321  
 322      $link = html_writer::tag('a', $fullurl, array('href'=>str_replace('&amp;', '&', $fullurl)));
 323      $clicktoopen = get_string('clicktoopen', 'url', $link);
 324      $moodleurl = new moodle_url($fullurl);
 325  
 326      $extension = resourcelib_get_extension($url->externalurl);
 327  
 328      $mediamanager = core_media_manager::instance($PAGE);
 329      $embedoptions = array(
 330          core_media_manager::OPTION_TRUSTED => true,
 331          core_media_manager::OPTION_BLOCK => true
 332      );
 333  
 334      if (in_array($mimetype, array('image/gif','image/jpeg','image/png'))) {  // It's an image
 335          $code = resourcelib_embed_image($fullurl, $title);
 336  
 337      } else if ($mediamanager->can_embed_url($moodleurl, $embedoptions)) {
 338          // Media (audio/video) file.
 339          $code = $mediamanager->embed_url($moodleurl, $title, 0, 0, $embedoptions);
 340  
 341      } else {
 342          // anything else - just try object tag enlarged as much as possible
 343          $code = resourcelib_embed_general($fullurl, $title, $clicktoopen, $mimetype);
 344      }
 345  
 346      url_print_header($url, $cm, $course);
 347      url_print_heading($url, $cm, $course);
 348  
 349      // Display any activity information (eg completion requirements / dates).
 350      $cminfo = cm_info::create($cm);
 351      $completiondetails = \core_completion\cm_completion_details::get_instance($cminfo, $USER->id);
 352      $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id);
 353      echo $OUTPUT->activity_information($cminfo, $completiondetails, $activitydates);
 354  
 355      echo $code;
 356  
 357      url_print_intro($url, $cm, $course);
 358  
 359      echo $OUTPUT->footer();
 360      die;
 361  }
 362  
 363  /**
 364   * Decide the best display format.
 365   * @param object $url
 366   * @return int display type constant
 367   */
 368  function url_get_final_display_type($url) {
 369      global $CFG;
 370  
 371      if ($url->display != RESOURCELIB_DISPLAY_AUTO) {
 372          return $url->display;
 373      }
 374  
 375      // detect links to local moodle pages
 376      if (strpos($url->externalurl, $CFG->wwwroot) === 0) {
 377          if (strpos($url->externalurl, 'file.php') === false and strpos($url->externalurl, '.php') !== false ) {
 378              // most probably our moodle page with navigation
 379              return RESOURCELIB_DISPLAY_OPEN;
 380          }
 381      }
 382  
 383      // Binaries and other formats that are known to cause trouble for external links.
 384      static $download = ['application/zip', 'application/x-tar', 'application/g-zip',
 385                          'application/pdf', 'text/html', 'document/unknown'];
 386      static $embed    = array('image/gif', 'image/jpeg', 'image/png', 'image/svg+xml',         // images
 387                               'application/x-shockwave-flash', 'video/x-flv', 'video/x-ms-wm', // video formats
 388                               'video/quicktime', 'video/mpeg', 'video/mp4',
 389                               'audio/mp3', 'audio/x-realaudio-plugin', 'x-realaudio-plugin',   // audio formats,
 390                              );
 391  
 392      $mimetype = resourcelib_guess_url_mimetype($url->externalurl);
 393  
 394      if (in_array($mimetype, $download)) {
 395          return RESOURCELIB_DISPLAY_DOWNLOAD;
 396      }
 397      if (in_array($mimetype, $embed)) {
 398          return RESOURCELIB_DISPLAY_EMBED;
 399      }
 400  
 401      // let the browser deal with it somehow
 402      return RESOURCELIB_DISPLAY_OPEN;
 403  }
 404  
 405  /**
 406   * Get the parameters that may be appended to URL
 407   * @param object $config url module config options
 408   * @return array array describing opt groups
 409   */
 410  function url_get_variable_options($config) {
 411      global $CFG;
 412  
 413      $options = array();
 414      $options[''] = array('' => get_string('chooseavariable', 'url'));
 415  
 416      $options[get_string('course')] = array(
 417          'courseid'        => 'id',
 418          'coursefullname'  => get_string('fullnamecourse'),
 419          'courseshortname' => get_string('shortnamecourse'),
 420          'courseidnumber'  => get_string('idnumbercourse'),
 421          'coursesummary'   => get_string('summary'),
 422          'courseformat'    => get_string('format'),
 423      );
 424  
 425      $options[get_string('modulename', 'url')] = array(
 426          'urlinstance'     => 'id',
 427          'urlcmid'         => 'cmid',
 428          'urlname'         => get_string('name'),
 429          'urlidnumber'     => get_string('idnumbermod'),
 430      );
 431  
 432      $options[get_string('miscellaneous')] = array(
 433          'sitename'        => get_string('fullsitename'),
 434          'serverurl'       => get_string('serverurl', 'url'),
 435          'currenttime'     => get_string('time'),
 436          'lang'            => get_string('language'),
 437      );
 438      if (!empty($config->secretphrase)) {
 439          $options[get_string('miscellaneous')]['encryptedcode'] = get_string('encryptedcode');
 440      }
 441  
 442      $options[get_string('user')] = array(
 443          'userid'          => 'id',
 444          'userusername'    => get_string('username'),
 445          'useridnumber'    => get_string('idnumber'),
 446          'userfirstname'   => get_string('firstname'),
 447          'userlastname'    => get_string('lastname'),
 448          'userfullname'    => get_string('fullnameuser'),
 449          'useremail'       => get_string('email'),
 450          'userphone1'      => get_string('phone1'),
 451          'userphone2'      => get_string('phone2'),
 452          'userinstitution' => get_string('institution'),
 453          'userdepartment'  => get_string('department'),
 454          'useraddress'     => get_string('address'),
 455          'usercity'        => get_string('city'),
 456          'usertimezone'    => get_string('timezone'),
 457      );
 458  
 459      if ($config->rolesinparams) {
 460          $roles = role_fix_names(get_all_roles());
 461          $roleoptions = array();
 462          foreach ($roles as $role) {
 463              $roleoptions['course'.$role->shortname] = get_string('yourwordforx', '', $role->localname);
 464          }
 465          $options[get_string('roles')] = $roleoptions;
 466      }
 467  
 468      return $options;
 469  }
 470  
 471  /**
 472   * Get the parameter values that may be appended to URL
 473   * @param object $url module instance
 474   * @param object $cm
 475   * @param object $course
 476   * @param object $config module config options
 477   * @return array of parameter values
 478   */
 479  function url_get_variable_values($url, $cm, $course, $config) {
 480      global $USER, $CFG;
 481  
 482      $site = get_site();
 483  
 484      $coursecontext = context_course::instance($course->id);
 485  
 486      $values = array (
 487          'courseid'        => $course->id,
 488          'coursefullname'  => format_string($course->fullname, true, array('context' => $coursecontext)),
 489          'courseshortname' => format_string($course->shortname, true, array('context' => $coursecontext)),
 490          'courseidnumber'  => $course->idnumber,
 491          'coursesummary'   => $course->summary,
 492          'courseformat'    => $course->format,
 493          'lang'            => current_language(),
 494          'sitename'        => format_string($site->fullname, true, array('context' => $coursecontext)),
 495          'serverurl'       => $CFG->wwwroot,
 496          'currenttime'     => time(),
 497          'urlinstance'     => $url->id,
 498          'urlcmid'         => $cm->id,
 499          'urlname'         => format_string($url->name, true, array('context' => $coursecontext)),
 500          'urlidnumber'     => $cm->idnumber,
 501      );
 502  
 503      if (isloggedin()) {
 504          $values['userid']          = $USER->id;
 505          $values['userusername']    = $USER->username;
 506          $values['useridnumber']    = $USER->idnumber;
 507          $values['userfirstname']   = $USER->firstname;
 508          $values['userlastname']    = $USER->lastname;
 509          $values['userfullname']    = fullname($USER);
 510          $values['useremail']       = $USER->email;
 511          $values['userphone1']      = $USER->phone1;
 512          $values['userphone2']      = $USER->phone2;
 513          $values['userinstitution'] = $USER->institution;
 514          $values['userdepartment']  = $USER->department;
 515          $values['useraddress']     = $USER->address;
 516          $values['usercity']        = $USER->city;
 517          $now = new DateTime('now', core_date::get_user_timezone_object());
 518          $values['usertimezone']    = $now->getOffset() / 3600.0; // Value in hours for BC.
 519      }
 520  
 521      // weak imitation of Single-Sign-On, for backwards compatibility only
 522      // NOTE: login hack is not included in 2.0 any more, new contrib auth plugin
 523      //       needs to be createed if somebody needs the old functionality!
 524      if (!empty($config->secretphrase)) {
 525          $values['encryptedcode'] = url_get_encrypted_parameter($url, $config);
 526      }
 527  
 528      //hmm, this is pretty fragile and slow, why do we need it here??
 529      if ($config->rolesinparams) {
 530          $coursecontext = context_course::instance($course->id);
 531          $roles = role_fix_names(get_all_roles($coursecontext), $coursecontext, ROLENAME_ALIAS);
 532          foreach ($roles as $role) {
 533              $values['course'.$role->shortname] = $role->localname;
 534          }
 535      }
 536  
 537      return $values;
 538  }
 539  
 540  /**
 541   * BC internal function
 542   * @param object $url
 543   * @param object $config
 544   * @return string
 545   */
 546  function url_get_encrypted_parameter($url, $config) {
 547      global $CFG;
 548  
 549      if (file_exists("$CFG->dirroot/local/externserverfile.php")) {
 550          require_once("$CFG->dirroot/local/externserverfile.php");
 551          if (function_exists('extern_server_file')) {
 552              return extern_server_file($url, $config);
 553          }
 554      }
 555      return md5(getremoteaddr().$config->secretphrase);
 556  }
 557  
 558  /**
 559   * Optimised mimetype detection from general URL
 560   * @param $fullurl
 561   * @param int $size of the icon.
 562   * @return string|null mimetype or null when the filetype is not relevant.
 563   */
 564  function url_guess_icon($fullurl, $size = null) {
 565      global $CFG;
 566      require_once("$CFG->libdir/filelib.php");
 567  
 568      if (substr_count($fullurl, '/') < 3 or substr($fullurl, -1) === '/') {
 569          // Most probably default directory - index.php, index.html, etc. Return null because
 570          // we want to use the default module icon instead of the HTML file icon.
 571          return null;
 572      }
 573  
 574      try {
 575          // There can be some cases where the url is invalid making parse_url() to return false.
 576          // That will make moodle_url class to throw an exception, so we need to catch the exception to prevent errors.
 577          $moodleurl = new moodle_url($fullurl);
 578          $fullurl = $moodleurl->out_omit_querystring();
 579      } catch (\moodle_exception $e) {
 580          // If an exception is thrown, means the url is invalid. No need to log exception.
 581          return null;
 582      }
 583  
 584      $icon = file_extension_icon($fullurl, $size);
 585      $htmlicon = file_extension_icon('.htm', $size);
 586      $unknownicon = file_extension_icon('', $size);
 587      $phpicon = file_extension_icon('.php', $size); // Exception for php files.
 588  
 589      // We do not want to return those icon types, the module icon is more appropriate.
 590      if ($icon === $unknownicon || $icon === $htmlicon || $icon === $phpicon) {
 591          return null;
 592      }
 593  
 594      return $icon;
 595  }