Working on a link shortening script and I'm stumped. I figured the following code would function as needed however, I get an execution time out related to $str .= $charset[mt_rand(0, $count-1)];. I have scoured over the code several times, I can't find what I am doing wrong.
function randString($length, $charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
$str = '';
$count = strlen($charset);
while ($length--) {
$str .= $charset[mt_rand(0, $count-1)];
}
return $str;
}
function shrinkURL($url) {
$is_unique = false;
$num = 4;
$random_string = randString($num);
$count = 0;
while (!$is_unique) {
$q1 = "SELECT id FROM linkShortner WHERE short = '".$random_string."' LIMIT 1";
$result = mysql_query($q1) or die(mysql_error());
if ($result === false) // if you don't get a result, then you're good
$is_unique = true;
else // if you DO get a result, keep trying
$count++;
if ($count >= 10) {
$num = (strlen($random_string) + 1);
$random_string = randString($num);
$count = 0;
}
$random_string = randString($num);
}
$shortURL = "https://domain.com/l/".$random_string;
$q2 = "INSERT INTO linkShortner (id, destination, short, shorURL, creationDate) VALUES (NULL, '".$url."', '".$random_string."', '".$shortURL."', '".$DateTime."')";
$r2 = mysql_query($q2) or die(mysql_error());
return $shortURL;
}
$shortURL = shrinkURL('http://domain.com');
echo $shortURL;
Any help would be greatly appreciated, think maybe I am just burnt out.
My guess is that at some point your function randString() will be called with the $length argument being 0.
This would make:
while ($length--) {
$str .= $charset[mt_rand(0, $count-1)];
}
get stuck, because the first iteration would be while (-1), which is true. And then while (-2), which is also true.. etc etc etc.
I would change your while ( $length-- ) to while ( $length-- >= 0 )
Changed if ($result === false) to if (!mysql_num_rows($result)) and all is well. if ($result === false) is for mysqli which is not being used here.
Basically the shrinkURL function scours the DB for a matching string before trying to insert a unique string/row, if ($result === false) would never = false because the value of $result is MySQL_query($q1) therefore producing an endless loop.
Related
I am upgrading a codebase that makes use of pass by reference
Main function
function splitSqlFile(&$ret, $sql)
{
$sql = trim($sql);
$sql_len = strlen($sql);
$char = '';
$string_start = '';
$in_string = false;
for ($i = 0; $i < $sql_len; ++$i) {
$char = $sql[$i];
if ($in_string) {
for (;;) {
$i = strpos($sql, $string_start, $i);
if (!$i) {
$ret[] = $sql;
return true;
}else if ($string_start == '`' || $sql[$i-1] != '\\'){
......
}else {
......
} // end if...elseif...else
} // end for
}
else if ($char == ';') {
$ret[] = substr($sql, 0, $i);
$sql = ltrim(substr($sql, min($i + 1, $sql_len)));
$sql_len = strlen($sql);
if ($sql_len) {
$i = -1;
} else {
// The submited statement(s) end(s) here
return true;
}
}else if (($char == '"') || ($char == '\'') || ($char == '`')) {
$in_string = true;
$string_start = $char;
} // end else if (is start of string)
// for start of a comment (and remove this comment if found)...
else if ($char == '#' || ($char == ' ' && $i > 1 && $sql[$i-2] . $sql[$i-1] == '--')) {
......
if (!$end_of_comment) {
// no eol found after '#', add the parsed part to the returned
// array and exit
$ret[] = trim(substr($sql, 0, $i-1));
return true;
} else {
.....
} // end if...else
} // end else if (is comment)
} // end for
// add any rest to the returned array
if (!empty($sql) && trim($sql) != '') {
$ret[] = $sql;
}
return true;
}
Calling the function
$sqlUtility->splitSqlFile($pieces, $sql_query);
foreach ($pieces as $piece)
{
.......
}
If the above variable splitSqlFile(&$ret, $sql) have the "&" before it, the program does run successfully, but if it is removed, now splitSqlFile($ret, $sql), It will start returning the 'invalid argument supplied for foreach' error.and when I try using the "is_array" function to check if it is an array, the result is always "NULL".
Why you get the error:
By removing the & from $ret, you are no longer referencing the variable in the function call. In this case, $pieces. So when you do a foreach on $pieces after calling the function, it will error because $pieces is basically a null variable at that point.
function splitSqlFile(&$ret,$sql) {
$ret[] = 'stuff';
}
splitSqlFile($pieces,$sql);
// $pieces will be an array as 0 => 'stuff'
foreach ($pieces as $piece) { } // will not error
vs:
function splitSqlFile($ret,$sql) {
$ret[] = 'stuff';
}
splitSqlFile($pieces,$sql);
// $pieces will be a null variable, since it was never assigned anything
foreach ($pieces as $piece) { } // will error
Alternative to no reference:
So if you want to remove the & and no longer pass by reference, you have to do other changes to the function to get that value back out. And depending on the codebase, this could mean a whole lot of work everywhere that function is used!
Example:
function splitSqlFile($sql) {
$ret = [];
$ret[] = 'stuff';
return array('result'=>true,'ret'=>$ret);
}
// $result will contain multiple things to utilize
// if you will only need that variable once (does not accumulate)
$result = splitSqlFile($sql);
foreach ($result['pieces'] as $piece) { }
// if that variable is added by multiple calls, and displayed later... merge
$pieces = [];
$result = splitSqlFile($sql_1);
$pieces = array_merge($pieces,$result['pieces']);
$result = splitSqlFile($sql_2);
$pieces = array_merge($pieces,$result['pieces']);
foreach ($pieces as $piece) { }
A second example (passing in the array as you go... gets confusing):
function splitSqlFile($pieces_in,$sql) {
$pieces_in[] = 'stuff';
return array('result'=>true,'pieces_out'=>$pieces_in);
}
$pieces = [];
$result = splitSqlFile($pieces,$sql_1);
$pieces = $result['pieces_out'];
$result = splitSqlFile($pieces,$sql_2);
$pieces = $result['pieces_out'];
foreach ($pieces as $piece) { }
As you can see, not only does it change the return values that has to be dealt with, but it also changes how it is called. Again, if this function is used in a thousand places in the code... serious headaches!
Conclusion:
I would honestly keep the reference as it is. It was done that way to make accumulating debug data easier, and direct. Otherwise you have a lot of code changes to do toget rid of the reference.
However that can simply be my opinion on the matter.
I have array with show names like this:
$shows = array('morning_show_15_02_2014_part2.mp3',
'morning_show_15_02_2014_part1.mp3',
'morning_show_14_02_2014_part2.mp3',
'morning_show_14_02_2014_part1.mp3',
'morning_show_13_02_2014_part2.mp3',
'morning_show_13_02_2014_part1.mp3');
So the list look like:
morning_show_15_02_2014_part2.mp3
morning_show_15_02_2014_part1.mp3
morning_show_14_02_2014_part2.mp3
morning_show_14_02_2014_part1.mp3
morning_show_13_02_2014_part2.mp3
morning_show_13_02_2014_part1.mp3
This is what i get when i loop the directory.
But the list should look like this:
morning_show_15_02_2014_part1.mp3
morning_show_15_02_2014_part2.mp3
morning_show_14_02_2014_part1.mp3
morning_show_14_02_2014_part2.mp3
morning_show_13_02_2014_part1.mp3
morning_show_13_02_2014_part2.mp3
Still ordered by date, but part 1 is first and then comes part 2.
How can i get this list into right order?
Thank you for any help!
Resolved!
Code is prett nasty but i got what i was looking for:
public function getMp3ListAsJSONArray() {
$songs = array();
$mp3s = glob($this->files_path . '/*.mp3');
foreach ($mp3s as $key => $mp3Source) {
$mp3Source = basename($mp3Source);
$mp3Title = substr($mp3Source, 4);
$mp3Title = substr($mp3Title, 0, -4);
$mp3Title = basename($mp3Source, ".mp3");
$mp3Title = str_replace('_', ' ', $mp3Title);
$mp3Title = ucfirst($mp3Title);
$songs[$key]['title'] = $mp3Title;
$songs[$key]['mp3'] = urldecode($this->files_url . '/' . $mp3Source);
}
rsort($songs);
$pairCounter = 1;
$counter = 0;
foreach ($songs as $key => $value) {
$playlist[$pairCounter][] = $value;
$counter = $counter + 1;
if($counter == 2) {
$pairCounter = $pairCounter + 1;
$counter = 0;
}
}
foreach ($playlist as $show) {
$finalList[] = $show[1];
$finalList[] = $show[0];
}
$finalList = json_encode($finalList);
return $finalList;
}
Output is like i described in the topic.
Try to use array sort
Here is an example for you
http://techyline.com/php-sorting-array-with-unique-value/
You must definitely write your own string comparision function. Remember that you have 2 different comparisons. The first compares the first parts for the filenames as strings. The second part compares the numbers, where 20 comes after 2. This is a natural number sorting for the second part. The third part is after the last dot in the filename. This will be ignored.
<?php
function value_compare($a, $b) {
$result = 0;
$descending = TRUE;
$positionA = strpos($a, 'part');
$positionB = strpos($b, 'part');
if ($positionA === $positionB) {
$compareFirstPart = substr_compare($a, $b, 0, $positionA + 1);
if ($compareFirstPart === 0) {
$length = 0;
$offset = $positionA + strlen('part');
$positionDotA = strrpos($a, '.');
$positionDotB = strrpos($b, '.');
$part2A = '';
$part2B = '';
if ($positionDotA !== FALSE) {
$part2A = substr($a, $offset, $positionDotA);
} else {
$part2A = substr($a, $offset);
}
if ($positionDotB !== FALSE) {
$part2B = substr($b, $offset, $positionDotB);
} else {
$part2B = substr($b, $offset);
}
$result = strnatcmp($part2A, $part2B);
} else {
$result = $compareFirstPart;
if ($descending) {
$result = -$result;
}
}
}
return $result;
}
$shows = array('morning_show_15_02_2014_part2.mp3', 'morning_show_15_02_2014_part1.mp3', 'morning_show_14_02_2014_part2.mp3', 'morning_show_14_02_2014_part1.mp3', 'morning_show_13_02_2014_part2.mp3', 'morning_show_13_02_2014_part1.mp3');
usort($shows, 'value_compare');
var_dump($shows);
?>
I have to store into MySQL from PHP quite a lot; usually the content to be stored I have in arrays. So I have written a small set of helper functions that saved me a lot of time.
This is the main function store that stores $array into MySQL $table:
function store($array, $table, $limit = 1000, $mode = '') {
if (empty($array)) return FALSE;
$sql_insert = "INSERT INTO `".$table."` (";
$array_keys = array_keys($array);
$keys = array_keys($array[$array_keys[0]]);
foreach ($keys as $key) $sql_insert .= "`".$key."`,";
$sql_insert = replaceEND($sql_insert, ') VALUES ', 1);
$count = 1;
$sql = $sql_insert;
foreach ($array as $array_num => $row) {
$sql .= '(';
foreach ($row as $key => $value) {
$string = $value;
if (!is_null($value)) {
$sql .= "'".str_replace("'", "\'", $string)."',";
} else {
$sql .= "NULL,";
}
}
$sql = replaceEND($sql, '),', 1);
$count = checkInsert($sql, $count, $limit);
if ($count == 0) {
if ($mode == 'PRINT') echo $sql.'<br />';
$sql = $sql_insert;
}
$count++;
}
$last = lastInsert($sql);
if ($last) {
if ($mode == 'PRINT') echo $sql.'<br />';
}
return TRUE;
}
I am using the following helpers within this function -
To add the correct ending to the statement strings:
function replaceEnd($string, $replacer = ';', $number_of_chars = 1) {
return left($string, strlen($string) - $number_of_chars) . $replacer;
}
To check, if $limit is reached which will lead to execution of the statement:
function checkInsert($sql, $sqlcount, $sql_limit) {
if ($sqlcount >= $sql_limit) {
$sql = replaceEND($sql);
query($sql);
$sqlcount = 0;
}
return $sqlcount;
}
To check for the last insert after the loop is finished:
function lastInsert($sql, $sql_insert = '') {
if (right($sql, 1) != ';') {
if ($sql != $sql_insert) {
$sql = replaceEND($sql);
query($sql);
return TRUE;
}
}
return FALSE;
}
Finally, to execute my statements:
function query($sql) {
$result = mysql_query($sql);
if ($result === FALSE) {
## Error Logging
}
return $result;
}
Basically, there is nothing wrong with these functions. But there is one problem, that I just could not solve: As you see, $limit is set to 1000 by default. This is usually perfectly fine, but when it comes to insert statements with much content / many characters per line of the insert statement, I have to reduce $limit manually in order to avoid errors. My question: does anyone know a way to automatically check if the current insert statement has reached the maximum in order to be executed correctly without wasting time by executing it too early? This would solve two problems at once: 1. I could get rid of $limit and 2. It would minimize the execution time needed to store the array into MySQL. I have just not found any approach to check the maximum possible length of a string that represents a MySQL insert statement, that I could apply before executing the statement...!?
I have programmed this function
function bad_words($val){
global $pre;
$sql = mysql_query("SELECT * FROM " . $pre . "BAD_WORDS") or die(mysql_error());
$rs = mysql_fetch_assoc($sql);
if (mysql_num_rows($sql) > 0) {
$bad_words = $rs['BAD_WORD'];
$replace = $rs['REPLACE'];
}
$val = str_ireplace($bad_words, $replace, $val);
return $val;
}
BAD_WORDS Table (ID, BAD_WORD, REPLACE)
When I use this function it replaces word with id = 1 but doesn't replace the other words.
What am I missing?
You are fetching only the first row from the table. You have to use a loop to iterate through
all the rows in the result set.
function bad_words($val)
{
global $pre;
$sql = mysql_query("SELECT * FROM " . $pre . "BAD_WORDS") or die(mysql_error());
if (mysql_num_rows($sql) > 0) {
while($rs = mysql_fetch_assoc($sql)) {
$bad_words[] = $rs['BAD_WORD'];
$replace[] = $rs['REPLACE'];
}
}
$val = str_ireplace($bad_words, $replace, $val);
return $val;
}
Well, that is because you are not looping through the resulting rows, you are only executing code on the first one. Once you determine there is one or more rows (if (mysql_num_rows($sql) > 0)), You'll need to loop through the records one at a time and perform the action.
check this for more: http://php.net/manual/en/function.mysql-fetch-array.php
I have a code where it should check if the result equals to 8 it need to show something and if not it need to show something else and all of that happens inside of a while loop.
while ($row_fpages2 = mysql_fetch_array($result_fanpage2))
{
if ( $row_fpages2['client'] != NULL ) {
//GRAPHS
$sql = "SELECT likes, date FROM statistics_pages WHERE idnum = '".$idnum."' AND page_name = '".$row_fpages2['page_name']."' ORDER BY `statistics_pages`.`date` DESC LIMIT 8";
$result2 = mysql_query($sql) or die(mysql_error());
if ($result2) {
$data = array();
while ($row = mysql_fetch_assoc($result2)) {
$data[] = $row["likes"];
}
if ($result2 == 8) {
$c_data = count($data)-1;
$final = array();
for ($i = 0; $i < $c_data; $i++) {
$final[] = getZeroResult($data[$i], $data[$i+1]);
}
$data_string = join(",", $final);
$stats = '<img src="http://chart.apis.google.com/chart?chs=240x140&cht=ls&chd=t:0,0|'.$data_string.'&chg=20,20&chls=0.75,-1,-1|6,4,1&chm=o,FF9900,1,-1,7,-1|b,3399CC44,0,1,0"></img>';
} else {
$stats = '<img src="images/stats_un.jpg"></img>';
};
} else {
print('MySQL query failed with error: ' . mysql_error());
}
echo '...';
The problem is that the first output always showing the ( == 8) (even if it is not equals to 8) instead of the else output.
Then if i have 2 or more everything comes above the first one is correct but the first one is still showing the ( == 8).
Any help?
You do the following which is incorrect:
$result2 = mysql_query($sql) or die(mysql_error());
...
if ($result2 == 8) {
The return value of mysql_query is a resource not an int. What is that you are trying to do there ?
May be you would like to use
if(strlen($result2) == 8){
...
}
instead of
if($result2 == 8){
...
}
Hope that solves your problem.