So i wrote some code for making seo-friendly urls. These functions first make a seo-friendly slug and then if the slug already exists is the DB(in this case array) then they add a number with a dash next to it. if that also exists then they just +1 the number and then checks again and again...
eg. if i pass "title url" to the function. First it will convert it to "title-url" and if "title-url" already exists then it will add a number like "title-url-1" if it exists as well then it will +1 the number like "title-url-2" and then "title-url-3" and so on...
this is the code:
// CONVERTS STRING TO URL SLUG
function str_to_slug($str){
$str = strtolower(trim($str));
$str = preg_replace('/[^a-z0-9-]/', '-', $str);
$str = preg_replace('/-+/', "-", $str);
return $str;
}
// RETURN SLUG URL
function slug($title){
$ori_url = str_to_slug($title);
if( does_slug_exists($ori_url) ){
return loop_slug_number($ori_url, 1);
}
else{
return $ori_url;
}
}
// ADD NUMBER
function loop_slug_number($slug, $number){
if( does_slug_exists($slug.'-'.$number) ){
loop_slug_number($slug, $number++);
exit;
}
else{
return $slug.'-'.$number;
}
}
// CHECKS WHEATHER THE SLUG EXISTS IN THE DB
function does_slug_exists($slug){
$array = array("title", "title-0", "title-1", "title-2");
return (in_array($slug, $array)) ? true : false;
}
i think everything should work fine. but when i echo slug("title"); i'm getting
Fatal error: Maximum function nesting level of '100' reached, aborting!
error line number is in the function does_slug_exists() on the 'return' line.
(the array is just for example i will use db validation.)
also if i replace the array with:
$array = array("title", "title-0", "title-2", "title-3");
then i get title-1 back.
Where is the mistake?
Ignoring any comments about the code quality, the issue here is the post-increment of the $number variable. You can replace with:
return loop_slug_number($slug, ++$number);
However, I suggest that the entire function should be rewritten as a while loop as opposed to a pseudo-recursive function. In addition, it looks like a DB query is made upon each call of does_slug_exists(); I suggest you refactor this to make the query once and store the returned result set. Have a look at this example.
// ADD NUMBER
function loop_slug_number($slug, $number){
if( does_slug_exists($slug.'-'.$number) ){ loop_slug_number($slug, $number++); exit; }else{ return $slug.'-'.$number; }
}
This is really awful code. Instead of looping, use a while loop. Start the number at 0 and while the slug exists, increment the number.
I'm not sure about PHP, but in C you should do a ++number instead. The idea is that the number gets incremented after the function was called if you do a number++ and before if you do ++number.
.. the joys of increment/decrement operators...
Related
I have a function that explodes a URL into directories. If I visit this script at example.com/test/directory, it should return test
function URLPart($part) {
if ($part > 0) {
$url = explode("/", $_SERVER['PHP_SELF']);
return $url[$part - 1];
} else {
return $_SERVER['PHP_SELF'];
}
}
URLPart(1);
Why is this not happening? I get nothing!
The $_SERVER['PHP_SELF'] variable contains an initial /, e.g. "/test/directory".
You have to ltrim($_SERVER['PHP_SELF'],'/'), before.
$url = explode("/", ltrim($_SERVER['PHP_SELF'], '/'));
or just :
return $url[$part];
There are two reasons you get nothing:
Because a function call doesn't print to screen -- echo does. You need to echo your return value.
#Syscall is correct that there is a leading / with $_SERVER['PHP_SELF'] so that is also throwing off your element targeting. 1 - 1 will have you accessing the 0 element explode()'s array. (I'm actually not sure why you were subtracting 1 from $part.)
While I am here I'll offer some additional advice.
$part is expected to be an integer, but let's force that to be true with intval().
!$part is used in the conditional to determine if the value is 0
I've extended your sample path string to illuminate my next point. I recommend adding $part+3 as the 3rd parameter of the explode() call so that the function doesn't have to do any unnecessary work. For this case, we are accessing element 2 of the array, so jamming the remainder of the path into element 3 is of no consequence. (see demo's output for how this works out)
Before attempting to access an array element by key, it is good practice to check that the key exists first -- to avoid a Notice. This is the reason for the !isset() condition.
++$part means add one to $part.
I prefer to write the early returns (failures) first in my codes, and put the successful output as the lowest return. And as I said earlier, echo the return value.
Code: (Demo)
function URLPart($part){
if(!$part=intval($part)){ // convert to integer and check if zero
return '/example.com/test/directory/foo/bar/what'; // $_SERVER['PHP_SELF']
}else{
$url = explode("/",'/example.com/test/directory/foo/bar/what',$part+3); // $_SERVER['PHP_SELF']
var_export($url);
echo "\n\n";
// when $part=1, offset 2 is desired
if(!isset($url[++$part])){ // if incremented $part is an invalid / non-existent offset
return '/example.com/test/directory/foo/bar/what'; // change this to your needs
}else{
return $url[$part];
}
}
}
echo URLPart(1); // you must echo the return value if you want it to be printed to screen
Output:
array (
0 => '',
1 => 'example.com',
2 => 'test',
3 => 'directory/foo/bar/what',
)
test
I need a program in PHP, when I search a number in a given series like 1,3,7,15,31... and if it is present in the series then give output as the index on which it is present in series?
LIKE I HAVE DONE SOMETHING TO DO THIS BUT FAILED.
<?php
function test1($n) {
for($i=1;$i<=$n;$i=$c) {
$c =1 + (2 * $i);
}
}
function test2($p) {
global $c,$n;
$input=array(1);
$in=array_push($input,$c);
$k=array_search($p,$input);
$flipped = array_flip($k);
var_dump($flipped);
}
test1(1000000);
test2(45);
Like in this program I had made two functions and in FUNCTION test1 I made a formula to make the series 1,3,7,15,31,63,127.... and in FUNCTION test2 I insert a number in form of parameter and want to SEARCH that number in the series that I form above and the I want OUTPUT as the index of that number searched.
Also if the number is not present in the series then I want the output as the nearest number of the number I search.
HELP.!!!
Thank You
You've got a few problems with this code.
function test1($n) {
for($i=1;$i<=$n;$i=$c) {
$c =1 + (2 * $i);
}
}
The first problem here is that you don't do anything with $c each time you increment it. You should probably be pushing it into an array of series integers.
Secondly you don't return a result, so you can't actually use the series you would've created.
You could use something like this instead:
function test1($limit) {
$series = [];
for ($i = 1; $i <= $limit; $i = $i * 2 + 1) {
$series[] = $i;
}
return $series;
}
Next, your test2 function:
function test2($p) {
global $c,$n;
$input=array(1);
$in=array_push($input,$c);
$k=array_search($p,$input);
$flipped = array_flip($k);
var_dump($flipped);
}
Ok, first don't use global variables. Pass the ones you need in as arguments and again, return the result. To be perfectly honest, I'm not entirely sure what this function is supposed to do. All you need is the array_search call which "searches the array for a given value and returns the first corresponding key if successful".
For example:
function test2($series, $number) {
return array_search($number, $series);
}
Using these, you can do something like this:
$series = test1(1000000);
var_dump(test2($series, 45)); // bool(false)
var_dump(test2($series, 31)); // int(4)
Also if the number is not present in the series then I want the output as the nearest number of the number I search.
Ok, you'll need to write some custom logic for this. I suggest you run your array_search check, then if it returns false you loop through your series and check the following criteria:
The previous series entry is lower than your number
The next series entry is higher than your number
Then return whichever of those two has a smaller absolute difference when you subtract the series entry from your number.
I'm not going to write an example for this because it smells a bit like a school assignment, which I'm sure you're capable of doing =) good luck.
Here is my question: I am trying to create a random bar code for my application. I want to check that if that code is already in the column, then generate a new number. Check it again. If it's unique, return it to the caller, else generate again.
I am using a recursive function for this purpose. I have added numbers 1,2,3,4 inside my database so every time it runs. It has to show me 5,6,7,8,9 or 10.
Here is my function:
function generate_barcode(){
$barcode = rand(1,10);
$bquery = mysql_num_rows(mysql_query("SELECT * FROM stock_item WHERE barcode='$barcode'"));
if($bquery==1){
generate_barcode();
}else{
return $barcode;
}
}
And I just tested it like this:
$a = generate_barcode();
if(isset($a))
{
echo $a;
}
else
{
echo 'Not Set';
}
So the problem is that it is sometimes showing me "Not Set", but I want it to always generate a unique number. I am not inserting the data, so it's not a problem that all of the numbers are reserved.
Someone just guide me and let me know what is wrong with the code. I can use other approaches to do that, but I need to know what is wrong with the supplied code.
You need to return the generated number from your recursive call too, like:
function generate_barcode() {
$barcode = rand(1, 10);
$bquery = mysql_num_rows(mysql_query("SELECT * FROM stock_item WHERE barcode='$barcode'"));
if ($bquery == 1) {
return generate_barcode(); // changed!
}
else {
return $barcode;
}
}
(You should include some kind of exit for the case that all numbers are 'taken'. This current version will call itself recursively until the PHP recursion limit is reached and will then throw an error.)
A return statement passes a value back to the immediate caller of the current function's call-frame. In the case of recursion, this immediate caller can be another invocation of that same function.
You can counter this by doing the following:
Change:
generate_barcode();
to:
return generate_barcode();
Do it like this:
$hash = md5( microtime().rand(0, 1000) );
Adding a time component means it will pretty much be unique. Unless you have like 32^32 of them.
If it has to be just numbers, just use the pkey and add like 10000 on to it for looks. or such.
After careful analysis - there is nothing wrong with it, but:
$a = generate_barcode();
if(isset($a)) <<< this bit
See you return the unique value and then you say it's isset my unique value, and $a will always be set, because if it's not you recurse the function until it is, and then you return it. Therefore it is always set...
You are trying to do:
while(true) {
$barcode = rand(1,10);
$bquery = mysql_num_rows(mysql_query("SELECT * FROM stock_item WHERE barcode='$barcode'"));
if($bquery===0){
break;
}
}
echo $barcode;
However, this will obviously only work for 10 bar codes and leading to an endless loop after that - meaning it is not the right approach to create a large number of bar codes.
Instead I would suggest to use an auto_increment to generate the bar code.
Btw, the mysql extension is deprecated. Use mysqli or PDO for new code.
I wanted to allow only specific email domain. Actually I did it. What i wanted to ask why my first code did not work at all.
I am just trying to learn PHP so that the question may seem silly, sorry for that.
Here is my code:
function check_email_address($email) {
$checkmail = print_r (explode("#",$email));
$container = $checkmail[1];
if(strcmp($container, "gmail.com")) {
return true;
}else {
return false;
}
}
Check out the documentation for strcmp() , it will return 0 of the two strings are the same, so that's the check you want to be doing. Also, you're using print_r() when you shouldn't be, as mentioned by the other answerers.
Anyway, here's how I would have done the function - it's much simpler and uses only one line of code:
function check_email_address($email) {
return (strtolower(strstr($email, '#')) == 'gmail.com');
}
It uses the strstr() function and the strtolower() function to get the domain name and change it to lower case, and then it checks if it is gmail.com or not. It then returns the result of that comparison.
It's because you're using print_r. It doesn't do what you seem to expect from it at all. Remove it:
$checkmail = explode("#", $email);
You can find the docs about print_r here:
http://php.net/print_r
Besides that, you can just use the following (it's much shorter):
$parts = explode("#", $email);
return (strcmp($parts[1], "gmail.com") == 0);
The following row doesn't work as you think it does:
$checkmail = print_r (explode("#",$email));
This means that you're trying to assign the return value from print_r() into $checkmail, but it doesn't actually return anything (if you don't supply the second, optional parameter with the value true).
Even then, it would've gotten a string containing the array structure, and your $container would have taken the value r, as it's the second letter in Array.
Bottom line: if your row would've been without the call to print_r(), it would've been working as planned (as long as you made sure to compare the strcmp() versus 0, as it means that the strings are identical).
Edit:
Interesting enough, I just realized that this could be achieved with the use of substr() too:
<?php
//Did we find #gmail.com at the end?
if( strtolower(substr($email, -10)) == '#gmail.com' ) {
//Do something since it's an gmail.com-address
} else {
//Error handling here
}
?>
You want:
if(strcmp($container, "gmail.com")==0)
instead of
if(strcmp($container, "gmail.com"))
Oh! And no inlined print_r() of course.
Even better:
return strcmp($container, "gmail.com")==0;
No need for the print_r; explode returns a list. And in terms of style (at least, my style) no need to assign the Nth element of that list to another variable unless you intend to use it a lot elsewhere. Thus,
$c = explode('#',$mail);
if(strcmp($c[1],'gmail.com') == 0) return true;
return false;
I have a function which returns only one row as array.
I give a query as a parameter which will exactly gives only one row.
function getFields($query)
{
$t =& get_instance();
$ret = $t->db->query($query);
if(!$ret)return false;
else if(!$ret->num_rows()) return array();
else return $ret->row_array();
}
$ret = getFields('query string');
What i thought was ...
if there is an error then i can check it like if(!$res)//echo error
if it is empty i can check it like if(!count($res))// no rows
else i assume that there is a row and continue the process...
BUT
when there is an error false is returned. In if(count($ret)) gives 1.
If($ret) conditions fails (gives false) if i return as empty array.
//
$ret = getFields('query string');
if(!$fes)jerror('status,0,msg,dberror');
if(!count($fes))jerror('status,1,msg,no rows');
// continue execution when there atleast one row.
this code is called using ajax. so i return a json response.
why count gives 1 and if empty array gives false.
i just wanted to code with logical conditions instead of giving more relations conditions. just to reduce code.
Where can i get all these BUGGING stuff of php so that i can make sure i should not end up making logical errors like the above.
BUGGING - in the above sentence
bugging i referred as not a bug but
things bugs us. things which makes us
logical errors.
I edited this following code to include the following meanwhile i got this as the reply by https://stackoverflow.com/users/451672/andrew-dunn
i can do it like this but still i want to know why for the above explanation
if($fes===false)jerror();
if(!$fes)jsuccess('status,4');
"why count gives 1" - see http://docs.php.net/count:
If var is not an array [...] 1 will be returned.
"and if empty array gives false." - see http://docs.php.net/language.types.boolean#language.types.boolean.casting:
When converting to boolean, the following values are considered FALSE:
[...]
an array with zero elements
Why don't you just check if $ret === false first?
If $myVariable is not array, but empty, then
$count = count($myVariable);
gives 1.
For this situation I check whether the variable is "array" or "bool(false)". If output is array, I use count, if it isn't an array I set number to zero.
if (is_array($myVariable)) {
$count = count($myVariable);
} else {
$count = 0;
}