I was wondering, do too many IF statements bloat coding and when is it okay not to use them?
These two examples both work the same and I'm the only one editing / using the script. Am I teaching myself bad habits by not adding the IF statement?
if ($en['mm_place']) {
$tmp = explode(",", $en['mm_place']);
$en['mm_place'] = $tmp[0].", ".$tmp[1]." ".$tmp[2];
}
is the same as...
$tmp = explode(",", $en['mm_place']);
$en['mm_place'] = $tmp[0].", ".$tmp[1]." ".$tmp[2];
EDIT: using #Francis Avila example I came up with this...
if ($en['mm_wmeet']) {
$tmp = explode(",", $en['mm_wmeet']);
for ($i = 0; $i < count($tmp); $i++) {
$en['mm_wmeet'] = $tmp[$i];
}
}
In this particular example, they are not the same.
If $en['mm_place'] is empty, then $tmp will not have three elements, so your string construction will be bogus.
Actually what you need is probably this:
if (!empty($en['mm_place'])) { // depending on whether you know if this is set and must be a string.
$tmp = explode(',', $en['mm_place'], 3);
if (count($tmp)===3) {
$en['mm_place'] = "{$tmp[0]}, {$tmp[1]} {$tmp[2]}";
}
}
Run PHP with E_NOTICE set, and code in such a way that you don't get any notices. PHP requires an extraordinary amount of discipline to use safely and properly because it has so many sloppy misfeatures. Notices will inform you of most bad practices. You will probably end up using lots of if statements.
if they don't serve any purpose then yes, you're bloating.
In this kind of situation, where you are checking if an array element exists before operating on it, you should keep the if-statement in the code. Here it will only throw a notice if the element is missing, but in the future you definitely could have similar code that will crash if the element is not set.
Edit: Actually those two code samples are not the same. if $en['mm_place'] is null or not set, the first sample will leave it as such while the second will replace it with ", "
Related
I am trying to loop through all the php files listed in an array called $articleContents and extract the variables $articleTitle and $heroImage from each.
So far I have the following code:
$articleContents = array("article1.php", "article2.php"); // array of all file names
$articleInfo = [];
$size = count($articleContents);
for ($x = 0; $x <= $size; $x++) {
ob_start();
if (require_once('../articles/'.$articleContents[$x])) {
ob_end_clean();
$entry = array($articleContents[$x],$articleTitle,$heroImage);
array_push($articlesInfo, $entry);
}
The problem is, the php files visited in the loop have html, and I can't keep it from executing. I would like to get variables from each of these files without executing the html inside each one.
Also, the variables $articleTitle and $heroImage also exist at the top of the php file I'm working in, so I need to make sure the script knows I'm calling the variables in the external file and not the current one.
If this is not possible, can you please recommend an alternative method?
Thanks!
Don't do this.
Your PHP scripts should be for your application, not for your data. For your data, if you want to keep it file-based, use a separate file.
There are plenty of formats to choose from. JSON is quite popular. You can use PHP's built-in serialization as well, which has support for more PHP-native types but is not as portable to other frameworks.
A little hacky but seems to works:
$result = eval(
'return (function() {?>' .
file_get_contents('your_article.php') .
'return [\'articleTitle\' => $articleTitle, \'heroImage\' => $heroImage];})();'
);
Where your_article.php is something like:
<?php
$articleTitle = 'hola';
$heroImage = 'como te va';
The values are returned in the $result array.
Explanation:
Build a string of php code where the code in your article scripts are wrapped inside a function that returns an array with the values you want.
function() {
//code of your article.php
return ['articleTitle' => $articleTitle, 'heroImage' => $heroImage];
}
Maybe you must do some adaptations to the strings due <?php ?> tags placements.
Anyway, this stuff is ugly. I'm very sure that it can be refactored in some way.
Your problem (probably) comes down to using parentheses with require. See the example and note here.
Instead, format your code like this
$articlesInfo = []; // watch your spelling here
foreach ($articleContents as $file) {
ob_start();
if (require '../articles/' . $file) { // note, no parentheses around the path
$articlesInfo[] = [
$file,
$articleTitle,
$heroImage
];
}
ob_end_clean();
}
Update: I've tested this and it works just fine.
I am writing some code to create fields automatically, which will save me a load of time. I have got most of my code working, but I have came across one error with the code, which is preventing me from achieving my final goal.
The code is as follows:
while ($i <= $numFields) {
$type = "\$field{$i}_Data['type']";
$name = "\$field{$i}_Data['name']";
$placeholder = "\$field{$i}_Data['placeholder']";
$value = "\$field{$i}_Data['value']";
echo '<input type="'.$type.'" name="'.$name.'" placeholder="'.$placeholder.'" value="'.$value.'">';
$i++;
}
The $numFields variable is defined at the top of my script, and I have worked out that it is something to do with how I am setting the variables $type, $name etc.
The end result is to create inputs depending on properties set in variables at the top of the script, The only issue I am having is with the settings of the variables, as said above.
If any extra code/information is needed, feel free to ask.
Thank you.
NOTE - There is no physical PHP error, it's purely an error with this:
"\$field{$i}_Data['value']";
There are a few ways we could write this one out, but they are all extensions of variable expansion and/or variable-variables.
Basically, we just need to put the variable name in a string and then use that string as the variable (much like you're currently doing with $i inside the string):
$type = ${"field{$i}_Data"}['type'];
$name = ${"field{$i}_Data"}['name'];
// ...
However, if you don't mind an extra variable, this can be written more cleanly by saving it like so:
$data = ${"field{$i}_Data"};
$type = $data['type'];
$name = $data['name'];
// ...
I have this piece of php code:
$skill_amount = round(pow($rarity,1.25));
It should be noted that $rarity is derived from a query.
I am inputting values like 0,2,4,8,16,32,64 into it.
99% of the time it is working but a small amount of times my users are reporting huge values like:
13771, 77936
What could possibly be causing this?
"What could possibly be causing this?"
A large or unexpected value for $rarity.
If you can inspect all possible values of $rarity, you should do that. Otherwise, you could do some basic debugging.
if ($skill_amount > some sane value) {
// log $skill_amount & $rarity to a file or email
// maybe also some other investigative values, like stuff that assisted the derivation of $rarity
}
No trouble found in this script, so I would agree with the need for sanity checks on $rarity.
<?php // RAY_temp_amy_neville.php
error_reporting(E_ALL);
echo '<pre>';
$range = range(1, 4096);
foreach ($range as $rarity)
{
$skill_amount = round(pow($rarity,1.25));
$out[$rarity] = $skill_amount;
}
print_r($out);
I'm trying to parse a 6,000 line 500 KB file into an array so I can import the data into our system. The problem is that the script stops executing somewhere between lines 3000-4000. There are no breaks in the code, we use it on other imports. Any ideas on why this might be happening and what I can do to prevent it?
/**
* Takes a seperated value string and makes it an array
* #param $delimiter string The delimiter to be seperated by, usually a comma or tab
* #param $string string The string to seperate
* #return array The resulting array
*/
public function svToArray ($delimiter, $string) {
$x = 0;
$rowList = array();
$splitContent = preg_split("#\n+#", trim($string));
foreach ($splitContent as $key => $value) {
$newData = preg_split("#".$delimiter."#", $value);
if ($x == 0) {
$headerValues = array_values($newData);
} else {
$tempRow = array();
foreach ($newData as $rowColumnKey => $rowColumnValue) {
$tempRow[$headerValues[$rowColumnKey]] = $rowColumnValue;
}
$rowList[] = $tempRow;
}
$x++;
}
return $rowList;
}
UPDATE:
Error reporting is enabled. I've started using a file that's only 130KB at 1,500 lines and it does the same thing...
When I add debug code as in the following example nothing echoes at all unless I put an exit after the echo "test<br/>";
public function svToArray ($delimiter, $string) {
$x = 0;
$rowList = array();
$splitContent = preg_split("#\n+#", trim($string));
echo "test<br/>";
foreach ($splitContent as $key => $value) {
$newData = preg_split("#".$delimiter."#", $value);
if ($x == 0) {
$headerValues = array_values($newData);
} else {
$tempRow = array();
foreach ($newData as $rowColumnKey => $rowColumnValue) {
$tempRow[$headerValues[$rowColumnKey]] = $rowColumnValue;
}
$rowList[] = $tempRow;
}
$x++;
}
echo "test";
$this->tru->debug($rowList);
exit;
return $rowList;
}
UPDATE
If I comment out $tempRow[] = $rowColumnValue; then it echoes everything fine....
Probably it just timeouts. Does it always stop after X seconds?
Try setting the max execution time higher: set_time_limit(900) at the top of your pages.
You can check the max execution time in your phpinfo():
1. Create a new php page with
2. Search for max_execution_time
Have you looked at the files? Is there a line with too many delimiters? Also, what are all the "#" about?
My best guess is that you're hitting a line where $headerValues[$rowColumnKey] is not defined.
What about $rowColumnKey and $rowColumnValue ?
They are not defined in the function.
Make sure that you have error reporting set, put this on top of your php file:
ini_set('display_errors', true);
error_reporting(E_ALL);
Also you can extend the script's execution time:
ini_set('max_execution_time', 50000);
About the only time I've a php script die, with no output and no errors is when it runs out of memory. It doesn't look like your script would use much memory, but I would check to make sure that the memory limit for php is high enough.
Are you sure that you are iterating through the returned array instead of simply trying to print it? (The issue may be outside of the function rather than within it.)
Also...
`exit;
return $rowList;`
Try removing the 'exit;' line and see if that changes anything.
Also, $splitContent is not defined as an array before being used, but preg_split is going to return an array. (This should not impact your results, but it is safe to do so.)
Why are you using $key => $value pairs when you cannot be sure of what will be within the string? If we could see an small example of the content of $string, we might be able to adjust this function to work better.
The # should be replaced with /, but that may simply be a formatting issue.
$newData should be defined as an array before being used just to be safe even though that is not causing your issue.
If you can, increase maximum execution time in php.ini.. also increase the maximum memory amount for each instance.. checking error logs of your webserver could also help.
It appears as though there was too much output to the output buffer, causing the page to show nothing at all.
UPDATE:
Thank you all for your input. Some additional information.
It's really just a small chunk of markup (20 lines) I'm working with and had aimed to to leverage a regex to do the work.
I also do have the ability to hack up the script (an ecommerce one) to insert the classes as the navigation is built. I wanted to limit the number of hacks I have in place to keep things easier on myself when I go to update to the latest version of the software.
With that said, I'm pretty aware of my situation and the various options available to me. The first part of my regex works as expected. I posted really more or less to see if someone would say, "hey dummy, this is easy just change this....."
After coming close with a few of my efforts, it's more of the principle at this point. To just know (and learn) a solution exists for this problem. I also hate being beaten by a piece of code.
ORIGINAL:
I'm trying to leverage regular expressions to add a CSS a class to the first and last list items within an ordered list. I've tried a bunch of different ways but can't produce the results I'm looking for.
I've got a regular expression for the first list item but can't seem to figure a correct one out for the last. Here is what I'm working with:
$patterns = array('/<ul+([^<]*)<li/m', '/<([^<]*)(?<=<li)(.*)<\/ul>/s');
$replace = array('<ul$1<li class="first"','<li class="last"$2$3</ul>');
$navigation = preg_replace($patterns, $replace, $navigation);
Any help would be greatly appreciated.
Jamie Zawinski would have something to say about this...
Do you have a proper HTML parser? I don't know if there's anything like hpricot available for PHP, but that's the right way to deal with it. You could at least employ hpricot to do the first cleanup for you.
If you're actually generating the HTML -- do it there. It looks like you want to generate some navigation and have a .first and .last kind of thing on it. Take a step back and try that.
+1 to generating the right html as the best option.
But a completely different approach, which may or may not be acceptable to you: you could use javascript.
This uses jquery to make it easy ...
$(document).ready(
function() {
$('#id-of-ul:firstChild').addClass('first');
$('#id-of-ul:lastChild').addClass('last');
}
);
As I say, may or may not be any use in this case, but I think its a valid solution to the problem in some cases.
PS: You say ordered list, then give ul in your example. ol = ordered list, ul = unordered list
You wrote:
$patterns = array('/<ul+([^<]*)<li/m','/<([^<]*)(?<=<li)(.*)<\/ul>/s');
First pattern:
ul+ => you search something like ullll...
The m modifier is useless here, since you don't use ^ nor $.
Second pattern:
Using .* along with s is "dangerous", because you might select the whole document up to the last /ul of the page...
And well, I would just drop s modifier and use: (<li\s)(.*?</li>\s*</ul>) with replace: '$1class="last" $2'
In view of above remarks, I would write the first expression: <ul.*?>\s*<li
Although I am tired of seeing the Jamie Zawinski quote each time there is a regex question, Dustin is right in pointing you to a HTML parser (or just generating the right HTML from the start!): regexes and HTML doesn't mix well, because HTML syntax is complex, and unless you act on a well known machine generated output with very predictable result, you are prone to get something breaking in some cases.
I don't know if anyone cares any longer, but I have a solution that works in my simple test case (and I believe it should work in the general case).
First, let me point out two things: While PhiLho is right in that the s is "dangerous", since dots may match everything up to the final of the document, this may very well be what you want. It only becomes a problem with not well formed pages. Be careful with any such regex on large, manually written pages.
Second, php has a special meaning of backslashes, even in single quotes. Most regexen will perform well either way, but you should always double-escape them, just in case.
Now, here's my code:
<?php
$navigation='<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
<li>Beer</li>
<li>Water</li>
</ul>';
$patterns = array('/<ul.*?>\\s*<li/',
'/<li((.(?<!<li))*?<\\/ul>)/s');
$replace = array('$0 class="first"',
'<li class="last"$1');
$navigation = preg_replace($patterns, $replace, $navigation);
echo $navigation;
?>
This will output
<ul>
<li class="first">Coffee</li>
<li>Tea</li>
<li>Milk</li>
<li>Beer</li>
<li class="last">Water</li>
</ul>
This assumes no line feeds inside the opening <ul...> tag. If there are any, use the s modifier on the first expression too.
The magic happens in (.(?<!<li))*?. This will match any character (the dot) that is not the beginning of the string <li, repeated any amount of times (the *) in a non-greedy fashion (the ?).
Of course, the whole thing would have to be expanded if there is a chance the list items already have the class attribute set. Also, if there is only one list item, it will match twice, giving it two such attributes. At least for xhtml, this would break validation.
You could load the navigation in a SimpleXML object and work with that. This prevents you from breaking your markup with some crazy regex :)
As a preface .. this is waaay over-complicating things in most use-cases. Please see other answers for more sanity :)
Here is a little PHP class I wrote to solve a similar problem. It adds 'first', 'last' and any other classes you want. It will handle li's with no "class" attribute as well as those that already have some class(es).
<?php
/**
* Modify list items in pre-rendered html.
*
* Usage Example:
* $replaced_text = ListAlter::addClasses($original_html, array('cool', 'awsome'));
*/
class ListAlter {
private $classes = array();
private $classes_found = FALSE;
private $count = 0;
private $total = 0;
// No public instances.
private function __construct() {}
/**
* Adds 'first', 'last', and any extra classes you want.
*/
static function addClasses($html, $extra_classes = array()) {
$instance = new self();
$instance->classes = $extra_classes;
$total = preg_match_all('~<li([^>]*?)>~', $html, $matches);
$instance->total = $total ? $total : 0;
return preg_replace_callback('~<li([^>]*?)>~', array($instance, 'processListItem'), $html);
}
private function processListItem($matches) {
$this->count++;
$this->classes_found = FALSE;
$processed = preg_replace_callback('~(\w+)="(.*?)"~', array($this, 'appendClasses'), $matches[0]);
if (!$this->classes_found) {
$classes = $this->classes;
if ($this->count == 1) {
$classes[] = 'first';
}
if ($this->count == $this->total) {
$classes[] = 'last';
}
if (!empty($classes)) {
$processed = rtrim($matches[0], '>') . ' class="' . implode(' ', $classes) . '">';
}
}
return $processed;
}
private function appendClasses($matches) {
array_shift($matches);
list($name, $value) = $matches;
if ($name == 'class') {
$value = array_filter(explode(' ', $value));
$value = array_merge($value, $this->classes);
if ($this->count == 1) {
$value[] = 'first';
}
if ($this->count == $this->total) {
$value[] = 'last';
}
$value = implode(' ', $value);
$this->classes_found = TRUE;
}
return sprintf('%s="%s"', $name, $value);
}
}