<?php
/*
Plugin Name: Legacy MySQL Scanner
Description: Search for references to legacy PHP MySQL functions so they can be upgraded to mysqli
Version: 1.02
Requires at least: 3.08
*/

$GLOBALS['LMSCANNER_DIR'] = SCRIPT_DIR ."/plugins/";   // directory to scan
//$GLOBALS['LMSCANNER_DIR'] = SCRIPT_DIR ."/plugins/newsletterBuilder/"; // override default path

// DON'T UPDATE ANYTHING BELOW THIS LINE

$GLOBALS['LMSCANNER_SKIP_SUFFIXES'] = [ // these are mysql_* suffixes to skip because they're no related to legacy mysql_ functions
  'count','datetime','delete','do','escape','escapecsv','escapef','fetch','get','getcolstovaluesfromrequest','getvaluesascsv','get_lock',
  'get_query','insert','query_fetch_all_array','query_fetch_row_array','query_fetch_row_assoc','release_lock','select','select_count_from',
  'select_query','session_storage','update','where'
];
$GLOBALS['LMSCANNER_SKIP_SUFFIXES'] = array_map('strtolower', $GLOBALS['LMSCANNER_SKIP_SUFFIXES']);  // lowercase suffixes

// plugin actions
pluginAction_addHandlerAndLink('Scan plugins', 'lms_scanPlugins', 'admins');

//
function lms_scanPlugins() {

  // get files
  print "<h3>Scanning directory: <b>{$GLOBALS['LMSCANNER_DIR']}</b> (You can change this in the plugin)</h3>\n";
  print "Note: This is a reference only, click on the legacy function name and see PHP.net docs for details on specific replacement recommendations.<br/>\n";
  print "Note: And when making replacements, note that some end in brackets () and some don't.<br/>\n";
  print "<hr>\n"; 
  $phpFilepaths = scandir_recursive($GLOBALS['LMSCANNER_DIR'], "/\.php\z/i");

  // skip list
  
  // scan files
  $fileCounter = 0;
  $matchedFileCounter = 0;
  $globalFunctionList  = [];
  $fileSkipKeywords    = ['/aws_s3/','/legacyMysqlScanner.php']; // skip paths with these keywords
  foreach ($phpFilepaths as $phpFilepath) {
    
    // skip non-code files AND files with skip keywords
    if (preg_match("/\.ini\.php\z/i", $phpFilepath))            { continue; }
    if (preg_match("/\.defaultSqlData\.php\z/i", $phpFilepath)) { continue; }
    foreach ($fileSkipKeywords as $keyword) { if (contains($keyword, $phpFilepath)) { continue 2; }}
    
    // count files scanned
    $fileCounter++;
  
    // scan for mysql_ functions that need upgrading
    $code = file_get_contents($phpFilepath);
    $functionsFound = [];
    if (preg_match_all("/\b(mysql_(\w+))\s*\(/i", $code, $matches, PREG_SET_ORDER)) {
      foreach ($matches as $match) {
        list($matchedString, $function, $suffix) = $match; // eg: mysql_escapef, escapef
        if (in_array(strtolower($suffix), $GLOBALS['LMSCANNER_SKIP_SUFFIXES'])) { continue; }
        $functionsFound[] = $function;
        $globalFunctionList[] = $function;
      }
    }
    if (!$functionsFound) { continue; } // skip file if no matches

    // show relative filepath and matched functions
    print str_replace($GLOBALS['LMSCANNER_DIR'], "", $phpFilepath); //
    
    $functionsFound = array_unique($functionsFound);
    natcasesort($functionsFound);
    lms_showFunctionsAndReplacements($functionsFound);
    
    $matchedFileCounter++;
  }

  // show list of all matched functions
  if ($matchedFileCounter) { 
    print "\n<hr><b>Matched function names</b>\n";
    $globalFunctionList = array_unique($globalFunctionList);
    natcasesort($globalFunctionList);
    lms_showFunctionsAndReplacements($globalFunctionList);
  }
  
  //
  if (!$matchedFileCounter) {
    print "No PHP files with legacy mysql code found!"; 
  }
  
  //
  print "\n<hr>\n"; 
  print "Files scanned: $fileCounter<br/>\n"; 
  print "Execute time: "  .showExecuteSeconds(true). " seconds<br/>\n"; 
  print "Done";
  exit;

}


//
function lms_showFunctionsAndReplacements($functions) {
  if (!$functions) { return; }
  
  print "<ul>\n";
  foreach ($functions as $function) {
      $instructions = ""; 
      $replacement = "";
      if ($function == 'mysql_affected_rows')      { $replacement  = "mysqli()->affected_rows;"; }
      if ($function == 'mysql_close')              { $instructions = "mysqli()->close();"; }
      if ($function == 'mysql_connect')            { $replacement  = "See connectToMySQL() and http://php.net/manual/en/mysqli.quickstart.connections.php"; }
      if ($function == 'mysql_error')              { $replacement  = "mysqli()->error;"; }
      if ($function == 'mysql_fetch_array')        { $replacement  = "mysqli_fetch_array(\$result);"; }
      if ($function == 'mysql_fetch_row')          { $replacement  = "mysqli_fetch_row(\$result);"; }
      if ($function == 'mysql_fetch_assoc')        { $replacement  = "mysqli_fetch_assoc(\$result);"; }
      if ($function == 'mysql_free_result')        { $replacement  = "mysqli_free_result(\$result);"; }
      if ($function == 'mysql_insert_id')          { $replacement  = "mysqli()->insert_id;"; }
      if ($function == 'mysql_num_rows')           { $replacement  = "mysqli_num_rows(\$result);"; }
      if ($function == 'mysql_ping')               { $replacement  = "mysqli()->ping();"; }
      if ($function == 'mysql_real_escape_string') { $replacement  = "mysql_escape(...);"; }
      if ($function == 'mysql_query')              { $replacement  = "mysqli()->query(...);"; }
      if ($function == 'mysql_result')             { $instructions = "Example: Replace \$value = mysql_result(\$result, 1, 3) with: mysqli_data_seek(\$result, 1); \$value = mysqli_fetch_array(\$result)[3]; // test after replacing!"; }
      if ($function == 'mysql_select_db')          { $replacement  = "mysqli()->select_db (...);"; }

      if ($replacement) { $instructions .= "replace with: $replacement"; }
      print "<li><a href='http://php.net/$function' target='_blank'>$function</a> - $instructions</li>";
  } 
  print "</ul>\n";

}

// eof