I'm having an issue with a PHP page that generates an HTML document. On the page, there is a form that has a <select> box, and each <option> inside has a value with the URL parameters to use when changing the window location when the form is "submitted". The issue is that I noticed is that one of the parameters is a name, and when that name has a space in it, it breaks the window location because the space remains in the URL.
I've tried simply doing a str_replace() on the string before it generates the <option> tag, and when I view the <option> in Firefox' inspector, it DOES contain a %20 instead of a space, but when I look at the URL bar after clicking the <option>, it still retains the space instead of the %20. Can anyone tell me what's wrong with the following snippet?
print("<form name=sel1>");
print("<select size=10 style='width:200;font-family:courier,monospace;
font-weight:bold;color:black;' ");
print("onchange=\"location = this.options[this.selectedIndex].value;\">");
for($i = 0; $i < count($allGroups); $i++)
{
print("<option value='groups.php?action=");
if($advancedPermissions)
{
if($modGroups)
{
print("edit");
}
else
{
print("view");
}
}
else
{
print("edit");
}
print("&group_id=");
print(str_replace(" ", "%20", $allGroups[$i][0])."'>");
print($allGroups[$i][0]);
if($allGroups[$i][2] != 'Y')
{
print(" - inactive");
}
}
print("</select></form>");
The relevant lines are the line with location = and the line just after the group_id parameter is added, where the str_replace() is done.
I do the str_replace() on just the value, not the display text, so it will show normally to the user, but contain the %20 character for when it is passed to the window location, but regardless, it either ignores it, or something else is happening I am not aware of.
This code is a whole mess of bad practices. First, separation of code (PHP) and presentation (HTML) is essential for making sense of things. You should be doing logic in a separate code block at the very least, if not a separate file. Definitely not in the middle of an HTML element. Exiting PHP and using alternative syntax and short echo tags make this separation much clearer.
You should be building HTTP query strings using the http_build_query() function, which will handle escaping issues like the one you're having, and you should always escape HTML for output using htmlspecialchars().
print is not commonly used, but note that it's a language construct and not a function. Parentheses are not needed, and rarely used.
Inline CSS and JavaScript declarations are very 20th century and should be avoided wherever possible.
Here's how I would start to rework this code...
<?php
// assuming $allGroups is created in a loop
// the following code could be incorporated into that loop
$options = [];
foreach ($allGroups as $group) {
$action = ($advancedPermissions && !$modGroups) ? "view" : "edit";
$group_id = $group[0];
$url = "groups.php?" . http_build_query(compact("action", "group_id"));
$options[$url] = $group[0] . ($group[4] !== "Y" ? " - inactive" : "");
}
?>
<!doctype html>
<html>
<head>
<style>
#location_select {
width: 200px; font-family: monospace; font-weight: bold; color: black;
}
</style>
</head>
<body>
<form name=sel1>
<select id="location_select" size="10">
<?php foreach ($options as $value => $option): ?>
<option value="<?= htmlspecialchars($value) ?>">
<?= htmlspecialchars($option) ?>
</option>
<?php endforeach ?>
</select>
</form>
<script>
document
.getElementById("location_selector")
.addEventListener("change", function() {
window.location = this.options[this.selectedIndex].value;
});
</script>
</body>
</html>
But if you are looking for the quick fix:
for($i = 0; $i < count($allGroups); $i++)
{
$action = ($advancedPermissions && !$modGroups) ? "view" : "edit";
$group_id = $allGroups[$i][0];
$url = "groups.php?" . http_build_query(compact("action", "group_id"));
print("<option value='$url'>");
print($allGroups[$i][0]);
if($allGroups[$i][2] != 'Y')
{
print(" - inactive");
}
print("</option>");
}
Related
This question already has answers here:
Hiding a Div using php
(6 answers)
Closed 1 year ago.
I want to show and hide a div based on a condition. This time I am not using any function since I thought it would be better as simple as possible.
I am trying to show and hide 2 DIVs based on a "status" of the user ($new). I don't know if it's possible to assign a PHP value to a JavaScript variable and the best way to do it ...
"var" is supposed to get the value of "$new".
Javascript:
<script>
var var = $new;
if (var != 1) {
document.getElementById("subjectr").style.display = "block";
} else {
document.getElementById("subjectr").style.display = "none";
}
</script>
HTML:
<center>
<div id="subjectr" value="<?php echo "$new"?>" style="display: none">
COORDINADOR
</div>
</center>
If you know a better way to do it, I would appreciate your help.
You don't need JS, simply echo or wrap the desired HTML into an if $new is true right on the server-side
Using PHP
<?php if ($new) { ?>
<div>Content for new users</div>
<?php } ?>
Using CSS (and PHP)
<div class="<?= $new ? '' : 'isHidden' ?>">Content for new users</div>
.isHidden { display: none; } /* Add this class to CSS file */
The above might come handy if at some point, by using JavaScript you want to toggle the visibility of such element using .classList.toggle('isHidden') or jQuery's .toggleClass('isHidden')
Using JavaScript (and PHP)
If you really want to pass your PHP variable to JavaScript:
<div id="subjectr">Content for new users</div>
<script>
var isNew = <?php echo json_encode($new); ?>;
document.getElementById("subjectr").style.display = isNew ? "block" : "none";
</script>
<!-- The above goes right before the closing </body> tag. -->
PS:
The <center> tag might work in some browsers but it's long time obsolete. Use CSS instead.
The value is an invalid HTML5 attribute for div Element. Use data-value instead.
What's doing the method=POST; on an <a> tag?
var var is a syntax Error in JavaScript, var being a reserved word. Use a more descriptive var isNew instead.
Instead of using javascript, you could use PHP to influence the display style of your div by using a ternary to decide whether it is none or block:
<center>
<div id="subjectr" value="<?=$new?>" style="display: <?=$new != 1 ? 'block' : 'none'?>">
COORDINADOR
</div>
</center>
For first, you should avoid to use var as a variable in your JS code, since it's an reserved key.
For your problem, revise your JS code:
<script>
var cond = document.getElementById('subjectr').getAttribute('value');
if (cond != 1) {
document.getElementById("subjectr").style.display = "block";
}
else {
document.getElementById("subjectr").style.display = "none";
}
</script>
You can get attribute result using getAttribute method in javascript, or attr method in jQuery.
A javascript variable can get the value of a PHP variable like so (assuming the PHP var is an integer):
var myvar = <?=$new?>;
Since you tag this as a PHP and a CSS issue, I think our mobile function is pretty near. This is our CSS "shownot" class:
.shownot { display: none !important; }
After, we use to hide certain cols on device mobiles, in your case the "$new" var:
<?php
$new = isMobile() ? 'shownot' : '' ;
?>
Finally, use as class whenever you need
<div id="subjectr" class="<?php echo $new;?>">
COORDINADOR
</div>
I’m making a random sentence generator for my English class. I’m close but because of my limited php and javascript knowledge I need to ask for help. I’m not bad at reading the code, I just get stuck writing it.
I want to use explode to break up a string of comma seperated values. The string is a mix of English and Spanish, on the .txt file they would seperated like:
The book, El libro
The man, El hombre
The woman, La mujer
etc.
I would like to break these two values into an array and display them in separate places on my web page.
I`m going to use a random text generator script that I found, it’s working great with no problems. I just need to modify it using explode to read, separate the values into an array, and be able to display the separate values of the array.
<?php
/* File, where the random text/quotes are stored one per line */
$settings['text_from_file'] = 'quotes.txt';
/*
How to display the text?
0 = raw mode: print the text as it is, when using RanTex as an include
1 = Javascript mode: when using Javascript to display the quote
*/
$settings['display_type'] = 1;
/* Allow on-the-fly settings override? 0 = NO, 1 = YES */
$settings['allow_otf'] = 1;
// Override type?
if ($settings['allow_otf'] && isset($_GET['type']))
{
$type = intval($_GET['type']);
}
else
{
$type = $settings['display_type'];
}
// Get a list of all text options
if ($settings['text_from_file'])
{
$settings['quotes'] = file($settings['text_from_file']);
}
// If we have any text choose a random one, otherwise show 'No text to choose from'
if (count($settings['quotes']))
{
$txt = $settings['quotes'][array_rand($settings['quotes'])];
}
else
{
$txt = 'No text to choose from';
}
// Output the image according to the selected type
if ($type)
{
// New lines will break Javascript, remove any and replace them with <br />
$txt = nl2br(trim($txt));
$txt = str_replace(array("\n","\r"),'',$txt);
// Set the correct MIME type
header("Content-type: text/javascript");
// Print the Javascript code
echo 'document.write(\''.addslashes($txt).'\')';
}
else
{
echo $txt;
}
?>
The script that displays the result:
<script type="text/javascript" src="rantex.php?type=1"></script>
Can someone please help me modify the rantex.php file so that I can use explode to separate the different comma separated values, and use a different script to call them in different places on my web page?
Thank you, and please excuse my noobness.
The following seems unnecessary, since file() will have already removed new line characters:
// New lines will break Javascript, remove any and replace them with <br />
$txt = nl2br(trim($txt));
$txt = str_replace(array("\n","\r"),'',$txt);
To break your line, you may instead use:
list($english, $spanish) = explode(', ', trim($txt));
It seems you are trying to use PHP to serve a static page with some random sentences, right? So why not use PHP to serve valid JSON, and handle to display logic on the client?
Heres a quick implementation.
// Get the data from the text file
$source = file_get_contents('./quotes.txt', true);
// Build an array (break on every line break)
$sentences = explode("\n", $source);
// Filter out empty values (if there is any)
$filtered = array_filter($sentences, function($item) {
return $item !== "";
});
// Build a hashmap of the array
$pairs = array_map(function($item) {
return ['sentence' => $item];
}, $filtered);
// Encode the hashmap to JSON, and return this to the client.
$json = json_encode($pairs);
Now you can let the client handle the rest, with some basic JavaScript.
// Return a random sentence from your list.
var random = sentences[Math.floor(Math.random() * sentences.length)];
// Finally display it
random.sentence
[edit]
You can get the JSON data to client in many ways, but if you don't want to use something like Ajax, you could simply just dump the contents on your webpage, then use JavaScript to update the random sentence, from the global window object.
// Inside your php page
<p>English: <span id="english"></span></p>
<p>Spanish: <span id="spanish"></span></p>
<script>
var sentences = <?= json_encode($pairs); ?>;
var random = sentences[Math.floor(Math.random() * sentences.length)];
var elspa = document.getElementById('spanish');
var eleng = document.getElementById('english');
elspa.innerText = random.sentence.split(',')[1];
eleng.innerText = random.sentence.split(',')[0];
</script>
Ok, so I have this figured out, I take 0 credit because I paid someone to do it. Special thanks to #stormpat for sending me in the right direction, if not for him I wouldn't have looked at this from a JSON point of view.
The .PHP file is like so:
<?php
$f_contents = file('quotes.txt');
$line = trim($f_contents[rand(0, count($f_contents) - 1)]);
$data = explode(',', $line);
$data['eng'] = $data[0];
$data['esp'] = $data[1];
echo json_encode($data);
?>
On the .HTML page in the header:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
(function ($) {
$(function()
{
function load_random_data() {
$.get('random_line.php', function(data) {
var data = $.parseJSON(data);
$('#random_english').text(data.eng);
$('#random_spanish').text(data.esp);
});
}
load_random_data();
$('#get_random').click(function(e){
e.preventDefault();
load_random_data();
});
});
})(jQuery);
</script>
This splits the different variables into classes, so to call them into my html page I call them by their class, for instance I wanted to drop the variable into a table cell so I gave the individual td cell a class:
<td id="random_spanish"></td>
<td id="random_english"></td>
Plus as a bonus the coder threw in a nifty button to refresh the json classes:
<input type="button" value="Get random" id="get_random" />
So now I don`t have to have my students refresh the whole web page, they can just hit the button and refresh the random variables.
Thanks again everyone!
im trying to pass a php array to javascript function onload that will display the js array in a drop down list but now im already doing it for sometime i guess i need to pop it again
first i pass it from one php file to another using this code
header("location: Rules.php?varFields=".serialize($varFields));
secondly i transfer to another variable as it had been passed to the said php file
<?php
$varArray = unserialize($_GET['varFields']);
?>
third part is im tyring to pass it into a jS functon that will then display it to a drop down list
<body id="body" onclick="cmbRuleField(\'' + <?php echo json_encode($varArray);?> + '\');" >
and here is the external javascript code
function cmbRuleField(varArray)//ruleField
{
var varDisplay = JSON.stringify(varArray);
var sel = document.getElementById("ruleField") // find the drop down
for (var i in varDisplay)
{ // loop through all elements
var opt = document.createElement("option"); // Create the new element
opt.value = varDisplay [i]; // set the value
opt.text = varDisplay [i]; // set the text
sel.appendChild(opt); // add it to the select
}
}
for the first two part i already tested it and it is working but for the last parts i cant make it work
I think this part looks suspicious
<body id="body" onclick="cmbRuleField(\'' + <?php echo json_encode($varArray);?> + '\');" >
maybe
<body id="body" onclick="cmbRuleField(<?php echo json_encode($varArray);?>)">
is more like it.
One more tip, you can see the output on the rendered page to determine what the written out code looks like. So if you see something like:
<body id="body" onclick="cmbRuleField('['a', 'b']')">
you know there is a problem. You want a native Javascript array to be passed like this
<body id="body" onclick="cmbRuleField(['a', 'b'])">
EDIT
After talking on chat it became clear the top portion of OP's code needed a tweak as well.
header("location: Rules.php?varFields=".http_build_query($varFields));
The problem has to do with quotes not being terminated here:
...
<body id="body" onclick="cmbRuleField(\'' + <?php echo json_encode($varArray);?> + '\');" >
...
The JSON created using json_encode will have a lot of double quotes. Try this:
<script>
var array = <?php echo json_encode($varArray);?>;
</script>
<body id="body" onclick="cmbRuleField(array);">
There is a much easier way. Encode the $varArray as direct HTML options before sending to the browser. For instance:
<select id="ruleField">
<?php for ($i = 0; $i < count($varArray); $i++) { ?>
<option value="<?php= $varArray[$i].val ?>"><?php= $varArray[$i].name ?></option>
<?php } ?>
</select>
Might be because you are calling JSON.stringify on something that is already a string?
Also what is myCars?
..
for (var i in myCars)
..
Possibly rename it to varArray.
or rename varDisplay to varArray.
and lastly try calling JSON.parse instead:
function cmbRuleField(varArray)//ruleField
{
var varDisplay = JSON.parse(varArray);
var sel = document.getElementById("ruleField") // find the drop down
for (var i in myCars)
{ // loop through all elements
var opt = document.createElement("option"); // Create the new element
opt.value = varDisplay [i]; // set the value
opt.text = varDisplay [i]; // set the text
sel.appendChild(opt); // add it to the select
}
}
If that didn't work post the html output here so peeps can create a fiddle :)
I have this page where a text might change, and each part has it's color.
I could have done it with PHP, but it kinda seemed like it's not practical.
I tried to read some javascript and jquery, thought they would help, but nothing.
so, is there a practical way to do this ?
here's my code:
index.php
<?php
$k=1;
if($k==0){
echo"<h class=client> Client </h>";
}
else if($k==1){
echo
"<h class=designer> Designer</h>" ;
}
else if($k==2){
echo
"<h class=developer> Developer</h>";
}
else if($k==3){
echo
"<h class=designer> Designer</h> & <h class=developer> Developer</h>";
}
?>
stylesheet.css:
.client {
color: blue;
}
.designer {
color: yellow;
}
.developer {
color: red;
}
so the output should be a a Blue "client" or Yellow "designer" or Red "developer", or a Yellow "designer" and a Black "&" and a Yellow "designer", depending on the k.
Classes are written in quotes.
Try:
<h class='developer'> Developer</h>
The use of javascript is overkill, you should probably just stick to a php only solution.
As I mentioned in the comments, you could alter your database design so that it compliments your application.
It's hard to say the best way to do this without knowing in detail the workings of you program - but as I mentioned, as an example you could store a more semantic value for $k (i.e. "client" or "designer" or "designer,developer"), or you could have 4 different booleans for each of your "titles" - but again the best way really depends on how you will need to interact with this information across the application as a whole.
A quick implementation for the first suggestion could look like this:
// $k = "designer,developer";
if (strpos($k, ',') !== FALSE){
$titles = explode(",",$k);
$output = "";
foreach($titles as $title){
$output .= "<h class='". $title ."'>". ucfirst($title) ."</h> & ";
}
echo rtrim($output, ' & ');
} else {
echo "<h class='". $k ."'>". ucfirst($k) ."</h>";
}
Whenever we are fetching some user inputed content with some editing from the database or similar sources, we might retrieve the portion which only contains the opening tag but no closing.
This can hamper the website's current layout.
Is there a clientside or serverside way of fixing this?
Found a great answer for this one:
Use PHP 5 and use the loadHTML() method of the DOMDocument object. This auto parses badly formed HTML and a subsequent call to saveXML() will output the valid HTML. The DOM functions can be found here:
http://www.php.net/dom
The usage of this:
$doc = new DOMDocument();
$doc->loadHTML($yourText);
$yourText = $doc->saveHTML();
I have solution for php
<?php
// close opened html tags
function closetags ( $html )
{
#put all opened tags into an array
preg_match_all ( "#<([a-z]+)( .*)?(?!/)>#iU", $html, $result );
$openedtags = $result[1];
#put all closed tags into an array
preg_match_all ( "#</([a-z]+)>#iU", $html, $result );
$closedtags = $result[1];
$len_opened = count ( $openedtags );
# all tags are closed
if( count ( $closedtags ) == $len_opened )
{
return $html;
}
$openedtags = array_reverse ( $openedtags );
# close tags
for( $i = 0; $i < $len_opened; $i++ )
{
if ( !in_array ( $openedtags[$i], $closedtags ) )
{
$html .= "</" . $openedtags[$i] . ">";
}
else
{
unset ( $closedtags[array_search ( $openedtags[$i], $closedtags)] );
}
}
return $html;
}
// close opened html tags
?>
You can use this function like
<?php echo closetags("your content <p>test test"); ?>
You can use Tidy:
Tidy is a binding for the Tidy HTML clean and repair utility which allows you to not only clean and otherwise manipulate HTML documents, but also traverse the document tree.
or HTMLPurifier
HTML Purifier is a standards-compliant
HTML filter library written in
PHP. HTML Purifier will not only remove all malicious
code (better known as XSS) with a thoroughly audited,
secure yet permissive whitelist,
it will also make sure your documents are
standards compliant, something only achievable with a
comprehensive knowledge of W3C's specifications.
For HTML fragments, and working from KJS's answer I have had success with the following when the fragment has one root element:
$dom = new DOMDocument();
$dom->loadHTML($string);
$body = $dom->documentElement->firstChild->firstChild;
$string = $dom->saveHTML($body);
Without a root element this is possible (but seems to wrap only the first text child node in p tags in text <p>para</p> text):
$dom = new DOMDocument();
$dom->loadHTML($string);
$bodyChildNodes = $dom->documentElement->firstChild->childNodes;
$string = '';
foreach ($bodyChildNodes as $node){
$string .= $dom->saveHTML($node);
}
Or better yet, from PHP >= 5.4 and libxml >= 2.7.8 (2.7.7 for LIBXML_HTML_NOIMPLIED):
$dom = new DOMDocument();
// Load with no html/body tags and do not add a default dtd
$dom->loadHTML($string, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$string = $dom->saveHTML();
In addition to server-side tools like Tidy, you can also use the user's browser to do some of the cleanup for you. One of the really great things about innerHTML is that it will apply the same on-the-fly repair to dynamic content as it does to HTML pages. This code works pretty well (with two caveats) and nothing actually gets written to the page:
var divTemp = document.createElement('div');
divTemp.innerHTML = '<p id="myPara">these <i>tags aren\'t <strong> closed';
console.log(divTemp.innerHTML);
The caveats:
The different browsers will return different strings. This isn't so bad, except in the the case of IE, which will return capitalized tags and will strip the quotes from tag attributes, which will not pass validation. The solution here is to do some simple clean-up on the server side. But at least the document will be properly structured XML.
I suspect that you may have to put in a delay before reading the innerHTML -- give the browser a chance to digest the string -- or you risk getting back exactly what was put in. I just tried on IE8 and it looks like the string gets parsed immediately, but I'm not so sure on IE6. It would probably be best to read the innerHTML after a delay (or throw it into a setTimeout() to force it to the end of the queue).
I would recommend you take #Gordon's advice and use Tidy if you have access to it (it takes less work to implement) and failing that, use innerHTML and write your own tidy function in PHP.
And though this isn't part of your question, as this is for a CMS, consider also using the YUI 2 Rich Text Editor for stuff like this. It's fairly easy to implement, somewhat easy to customize, the interface is very familiar to most users, and it spits out perfectly valid code. There are several other off-the-shelf rich text editors out there, but YUI has the best license and is the most powerful I've seen.
A better PHP function to delete not open/not closed tags from webmaster-glossar.de (me)
function closetag($html){
$html_new = $html;
preg_match_all ( "#<([a-z]+)( .*)?(?!/)>#iU", $html, $result1);
preg_match_all ( "#</([a-z]+)>#iU", $html, $result2);
$results_start = $result1[1];
$results_end = $result2[1];
foreach($results_start AS $startag){
if(!in_array($startag, $results_end)){
$html_new = str_replace('<'.$startag.'>', '', $html_new);
}
}
foreach($results_end AS $endtag){
if(!in_array($endtag, $results_start)){
$html_new = str_replace('</'.$endtag.'>', '', $html_new);
}
}
return $html_new;
}
use this function like:
closetag('i <b>love</b> my <strike>cat');
#output: i <b>love</b> my cat
closetag('i <b>love</b> my cat</strike>');
#output: i <b>love</b> my cat
Erik Arvidsson wrote a nice HTML SAX parser in 2004. http://erik.eae.net/archives/2004/11/20/12.18.31/
It keeps track of the the open tags, so with a minimalistic SAX handler it's possible to insert closing tags at the correct position:
function tidyHTML(html) {
var output = '';
HTMLParser(html, {
comment: function(text) {
// filter html comments
},
chars: function(text) {
output += text;
},
start: function(tagName, attrs, unary) {
output += '<' + tagName;
for (var i = 0; i < attrs.length; i++) {
output += ' ' + attrs[i].name + '=';
if (attrs[i].value.indexOf('"') === -1) {
output += '"' + attrs[i].value + '"';
} else if (attrs[i].value.indexOf('\'') === -1) {
output += '\'' + attrs[i].value + '\'';
} else { // value contains " and ' so it cannot contain spaces
output += attrs[i].value;
}
}
output += '>';
},
end: function(tagName) {
output += '</' + tagName + '>';
}
});
return output;
}
I used to the native DOMDocument method, but with a few improvements for safety.
Note, other answers that use DOMDocument do not consider html strands such as
This is a <em>HTML</em> strand
The above will actually result in
<p>This is a <em>HTML</em> strand
My Solution is below
function closeDanglingTags($html) {
if (strpos($html, '<') || strpos($html, '>')) {
// There are definitiley HTML tags
$wrapped = false;
if (strpos(trim($html), '<') !== 0) {
// The HTML starts with a text node. Wrap it in an element with an id to prevent the software wrapping it with a <p>
// that we know nothing about and cannot safely retrieve
$html = cHE::getDivHtml($html, null, 'closedanglingtagswrapper');
$wrapped = true;
}
$doc = new DOMDocument();
$doc->encoding = 'utf-8';
#$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
if ($doc->firstChild) {
// Test whether the firstchild is definitely a DOMDocumentType
if ($doc->firstChild instanceof DOMDocumentType) {
// Remove the added doctype
$doc->removeChild($doc->firstChild);
}
}
if ($wrapped) {
// The contents originally started with a text node and was wrapped in a div#plasmappclibtextwrap. Take the contents
// out of that div
$node = $doc->getElementById('closedanglingtagswrapper');
$children = $node->childNodes; // The contents of the div. Equivalent to $('selector').children()
$doc = new DOMDocument(); // Create a new document to add the contents to, equiv. to "var doc = $('<html></html>');"
foreach ($children as $childnode) {
$doc->appendChild($doc->importNode($childnode, true)); // E.g. doc.append()
}
}
// Remove the added html,body tags
return trim(str_replace(array('<html><body>', '</body></html>'), '', html_entity_decode($doc->saveHTML())));
} else {
return $html;
}
}