The PHP MySQL extension is being deprecated in favor of the MySQLi extension in PHP 5.5, and removed as of PHP 7.0. MySQLi was first referenced in PHP v5.0.0 beta 4 on 2004-02-12, with the first stable release in PHP 5.0.0 on 2004-07-13[1]. Before that, the PHP MySQL extension was by far the most popular way of interacting with MySQL on PHP, and still was for a very long time after. This website was opened only 2 years after the first stable release!
With the deprecation, problems from some websites I help host have popped up, many of these sites being very, very old. I needed a quick and dirty solution to monkey-patch these websites to use MySQLi without rewriting all their code. The obvious answer is to overwrite the functions with wrappers for MySQLi. The generally known way of doing this is with the Advanced PHP Debugger (APD). However, using this extension has a lot of requirements that are not appropriate for a production web server. Fortunately, another extension I recently learned of offers the renaming functionality; runkit. It was a super simple install for me.
From the command line, run “pecl install runkit”
Add “extension=runkit.so” and “runkit.internal_override=On” to the php.ini
Besides the ability to override these functions with wrappers, I also needed a way to make sure this file was always loaded before all other PHP files. The simple solution for that is adding “auto_prepend_file=/PATH/TO/FILE” to the “.user.ini” in the user’s root web directory.
The code for this script is as follows. It only contains a limited set of the MySQL functions, including some very esoteric ones that the web site used. This is not a foolproof script, but it gets the job done.
//Override the MySQL functionsforeach(Array('connect', 'error', 'fetch_array', 'fetch_row', 'insert_id', 'num_fields', 'num_rows','query', 'select_db', 'field_len', 'field_name', 'field_type', 'list_dbs', 'list_fields','list_tables', 'tablename') as$FuncName) runkit_function_redefine("mysql_$FuncName", '','return call_user_func_array("mysql_'.$FuncName.'_OVERRIDE", func_get_args());');//If a connection is not explicitely passed to a mysql_ function, use the last created connectionglobal$SQLLink; //The remembered SQL LinkfunctionGetConn($PassedConn){if(isset($PassedConn))return$PassedConn;global$SQLLink;return$SQLLink;}//Override functionsfunctionmysql_connect_OVERRIDE($Host, $Username, $Password) {global$SQLLink;return$SQLLink=mysqli_connect($Host, $Username, $Password);}functionmysql_error_OVERRIDE($SQLConn=NULL) {return mysqli_error(GetConn($SQLConn));}functionmysql_fetch_array_OVERRIDE($Result, $ResultType=MYSQL_BOTH) {returnmysqli_fetch_array($Result, $ResultType);}functionmysql_fetch_row_OVERRIDE($Result) {returnmysqli_fetch_row($Result);}functionmysql_insert_id_OVERRIDE($SQLConn=NULL) {return mysqli_insert_id(GetConn($SQLConn));}functionmysql_num_fields_OVERRIDE($Result) {return mysqli_num_fields($Result);}functionmysql_num_rows_OVERRIDE($Result) {return mysqli_num_rows($Result);}functionmysql_query_OVERRIDE($Query, $SQLConn=NULL) {returnmysqli_query(GetConn($SQLConn), $Query);}functionmysql_select_db_OVERRIDE($DBName, $SQLConn=NULL) {returnmysqli_select_db(GetConn($SQLConn), $DBName);}functionmysql_field_len_OVERRIDE($Result, $Offset) {$Fields=$Result->fetch_fields();return$Fields[$Offset]->length;}functionmysql_field_name_OVERRIDE($Result, $Offset) {$Fields=$Result->fetch_fields();return$Fields[$Offset]->name;}functionmysql_field_type_OVERRIDE($Result, $Offset) {$Fields=$Result->fetch_fields();return$Fields[$Offset]->type;}functionmysql_list_dbs_OVERRIDE($SQLConn=NULL) {$Result=mysql_query('SHOW DATABASES', GetConn($SQLConn));$Tables=Array();while($Row=mysqli_fetch_assoc($Result))$Tables[]=$Row['Database'];return$Tables;}functionmysql_list_fields_OVERRIDE($DBName, $TableName, $SQLConn=NULL) {$SQLConn=GetConn($SQLConn);$CurDB=mysql_fetch_array(mysql_query('SELECT Database()', $SQLConn));$CurDB=$CurDB[0];mysql_select_db($DBName, $SQLConn);$Result=mysql_query("SHOW COLUMNS FROM $TableName", $SQLConn);mysql_select_db($CurDB, $SQLConn);if(!$Result) {print'Could not run query: '.mysql_error($SQLConn);returnArray(); }$Fields=Array();while($Row=mysqli_fetch_assoc($Result))$Fields[]=$Row['Field'];return$Fields;}functionmysql_list_tables_OVERRIDE($DBName, $SQLConn=NULL) {$SQLConn=GetConn($SQLConn);$CurDB=mysql_fetch_array(mysql_query('SELECT Database()', $SQLConn));$CurDB=$CurDB[0];mysql_select_db($DBName, $SQLConn);$Result=mysql_query("SHOW TABLES", $SQLConn);mysql_select_db($CurDB, $SQLConn);if(!$Result) {print'Could not run query: '.mysql_error($SQLConn);returnArray(); }$Tables=Array();while($Row=mysql_fetch_row($Result))$Tables[]=$Row[0];return$Tables;}functionmysql_tablename_OVERRIDE($Result) {$Fields=$Result->fetch_fields();return$Fields[0]->table;}
And here is some test code to confirm functionality:
global$MyConn, $TEST_Table;$TEST_Server='localhost';$TEST_UserName='...';$TEST_Password='...';$TEST_DB='...';$TEST_Table='...';functionGetResult() {global$MyConn, $TEST_Table;returnmysql_query('SELECT*FROM'.$TEST_Table.' LIMIT 1', $MyConn);}var_dump($MyConn=mysql_connect($TEST_Server, $TEST_UserName, $TEST_Password));//Set $MyConn to NULL here if you want to test global $SQLLink functionalityvar_dump(mysql_select_db($TEST_DB, $MyConn));var_dump(mysql_query('SELECT*FROM INVALIDTABLE LIMIT1', $MyConn));var_dump(mysql_error($MyConn));var_dump($Result=GetResult());var_dump(mysql_fetch_array($Result));$Result=GetResult(); var_dump(mysql_fetch_row($Result));$Result=GetResult(); var_dump(mysql_num_fields($Result));var_dump(mysql_num_rows($Result));var_dump(mysql_field_len($Result, 0));var_dump(mysql_field_name($Result, 0));var_dump(mysql_field_type($Result, 0));var_dump(mysql_tablename($Result));var_dump(mysql_list_dbs($MyConn));var_dump(mysql_list_fields($TEST_DB, $TEST_Table, $MyConn));var_dump(mysql_list_tables($TEST_DB, $MyConn));mysql_query('CREATE TEMPORARY TABLE mysqltest (i int auto_increment, primary key (i))', $MyConn);mysql_query('INSERT INTO mysqltest VALUES ()', $MyConn);mysql_query('INSERT INTO mysqltest VALUES ()', $MyConn);var_dump(mysql_insert_id($MyConn));mysql_query('DROP TEMPORARY TABLE mysqltest', $MyConn);
Takes raw input and wraps long lines to indent against the current line start.
When an indented/list line is encountered, which starts with spaces followed by a star "*", wrapped line’s indents will start 2 spaces after the star.
Lines attempt to split at words of 10 characters or less (see .MinCharsInSplitWord).
If a line needs to split along a word longer than this, a hyphen is inserted at the end of the line.
import argparse
import re
classBulletHelpFormatter(argparse.HelpFormatter):
def__init__(self, *args, **kwargs):
super(BulletHelpFormatter, self).__init__(*args, **kwargs)
self.MinCharsInSplitWord=10def_split_lines(self, text, width):
#Split lines around line breaks and then modify each line
Lines=[]
for Line in text.splitlines():
#Get the number of spaces to put at subsequent lines#0 if not a list item, oherwise, 2+list item start
ListEl=re.match(r'^*\*', Line)
NumBeginningSpace=(0if ListEl==Noneelse ListEl.end()+1)
#Add extra spaces at the beginning of each line to match the start of the current line, and go to a maxium of $width
IsFirstPass=True
SpacesToAdd=''
NumSpacesToAdd=0while(True):
#Get the word break points before and after where the line would end
MaxLineLen=max(min(width-NumSpacesToAdd, len(Line)), 1)
PrevWordBreak=CurWordBreak=0for WordBreak in re.finditer(r'(?<=\W).|\W|$', Line):
PrevWordBreak=CurWordBreak
CurWordBreak=WordBreak.start()
if CurWordBreak>=MaxLineLen:
if CurWordBreak==MaxLineLen:
PrevWordBreak=CurWordBreak
break#If previous wordbreak is more than MinCharsInSplitWord away from MaxLineLen, then split at the end of the line
IsSplit=(PrevWordBreak<1or CurWordBreak-PrevWordBreak>self.MinCharsInSplitWord)
SplitPos=(MaxLineLen if IsSplit else PrevWordBreak)
#Append the new line to the list of lines
Lines.append(SpacesToAdd+Line[0:SplitPos]+('-'if IsSplit else''))
Line=Line[SplitPos:]
#If this is the end, nothing left to doiflen(Line)==0:
break#If this is the first pass, update line creation variablesif IsFirstPass:
IsFirstPass=False
NumSpacesToAdd=NumBeginningSpace
SpacesToAdd=(''* NumSpacesToAdd)
return Lines