The โabove the foldโ reference in todayโs blog posting title harkens back to the part of the (what was initially a newspaper, but more often these days a screen) content immediately viewable by the reader without any other action from them to renew that content in any way. Yes, it is relative, and today we try to turn our Online HTML Validator web application to be more often โabove the foldโ using this MacBook Air screen, but the exercise of trying to do this will normally improve web application usability for other platforms as well.
In order to help here, we โflattenedโ (horizontally) the HTML form table down the bottom of the screen, that also helps clarify that difference between โฆ
- tidy โฆ or โฆ
- DOMDocument
โฆ PHP validation methodologies.
Also, today, on top of yesterdayโs HTML Online Validation via Error Array Tutorialโs progress we allow any one DOMDocument error line contains multiple error columns, as applicable, both being flagged in the user inputted HTML element presented, as well โฆ
<?php
$qs=['❓', '❗', '➕', '❌'];
$iqs=0;
$errarr=[];
$errline=[];
$lastline=-1;
foreach (libxml_get_errors() as $error) { // thanks to https://www.php.net/manual/en/function.libxml-get-errors.php
if ($error->line != $lastline) {
$iqs=0;
if (sizeof($errline) != 0) {
$lastone=$errarr[-1 + sizeof($errarr)];
for ($jj=(-1 + sizeof($errline)); $jj>=0; $jj--) {
$errarr[-1 + sizeof($errarr)]=$errline[$jj];
}
$errarr[sizeof($errarr)]=$lastone;
$errline=[];
}
$errarr[sizeof($errarr)]=$error;
$lastline=$error->line;
} else {
$iqs++;
$errline[sizeof($errline)]=$error;
}
if ($emore == '') { $emore.='<br><br><table border=2 cellpadding=5 cellspacing=5 style=background-color:#ffffe0;><tr><td>' . $within . '</td><td>'; }
switch ($error->level) {
case LIBXML_ERR_WARNING:
$emore .= "DOMDocument::loadHTML(); Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$emore .= "DOMDocument::loadHTML(); Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$emore .= "DOMDocument::loadHTML(); Fatal Error $error->code: ";
break;
}
$emore.=$error->message . ' at line ' . $error->line . ' <SUB>' . $error->line . '</SUB>, column ' . $error->column . ' <SUP>' . $qs[($iqs % sizeof($qs))] . '</SUP><br>';
}
if (sizeof($errline) != 0) {
$lastone=$errarr[sizeof($errarr)];
for ($jj=(-1 + sizeof($errline)); $ii>=0; $ii--) {
$errarr[sizeof($errarr)]=$errline[$jj];
}
$errarr[sizeof($errarr)]=$lastone;
$errline=[];
}
$iqs=0;
for ($ii=0; $ii<sizeof($errarr); $ii++) {
$error=$errarr[$ii];
// handle errors here
if ($error->column == 0) {
$betterbws[-1 + $error->line]='<SUB>' . $error->line . '</SUB><SUP>' . $qs[($iqs % sizeof($qs))] . '</SUP>' . $betterbws[-1 + $error->line];
} else {
$betterbws[-1 + $error->line]=substr($betterbws[-1 + $error->line],0,(-1 + $error->column)) . '<SUB>' . $error->line . '</SUB><SUP>' . $qs[($iqs % sizeof($qs))] . '</SUP>' . substr($betterbws[-1 + $error->line],(-1 + $error->column));
}
if (($ii + 1) == sizeof($errarr)) {
$iqs=0;
$betterwithin=str_replace($bws[-1 + $error->line], $betterbws[-1 + $error->line], $betterwithin);
} else if ($errarr[$ii + 1]->line != $errarr[$ii]->line) {
$iqs=0;
$betterwithin=str_replace($bws[-1 + $error->line], $betterbws[-1 + $error->line], $betterwithin);
} else {
$iqs++;
}
}
libxml_clear_errors();
if ($emore != '') {
$emore=str_replace($within, str_replace('</SUP>','</sup>',str_replace('<SUP>','<sup>',str_replace('</SUB>','</sub>',str_replace('<SUB>','<sub>',str_replace('<BR>','<br>',str_replace('>','>',str_replace('<','<',$betterwithin))))))), $emore) . '</td></tr></table><br>';
}
$tec='No tidy module detected.' . $emore;
?>
โฆ with thechanged tidy_examplephpโs proof
of concept PHP online HTML validator web application you can try here also, the mobile platform versions of which only allow the DOMDocument methodology be used.
Previous relevant HTML Online Validation via Error Array Tutorial is shown below.
Todayโs job is to sharpen up the DOMDocument Online HTML Validation error reporting, onto yesterdayโs HTML Online Validation via PHP Rendering Tutorial by โฆ
- organizing the error messages to appear in a right hand column of a two column table, rather than up the top before any HTML โฆ and โฆ
- via new error processing code โฆ
<?php
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->validateOnParse = true;
$doc->loadHTML($htmlin);
$betterwithin=str_replace("\n"," <BR>",$htmlin);
$within=str_replace("\n"," <BR>",str_replace('>','~',str_replace('<','`',$htmlin)));
$bws=explode("<BR>", $betterwithin);
$betterbws=explode("<BR>", $betterwithin);
foreach (libxml_get_errors() as $error) { // thanks to https://www.php.net/manual/en/function.libxml-get-errors.php
// handle errors here
if ($emore == '') { $emore.='<br><br><table border=2 cellpadding=5 cellspacing=5 style=background-color:#ffffe0;><tr><td>' . $within . '</td><td>'; }
switch ($error->level) {
case LIBXML_ERR_WARNING:
$emore .= "DOMDocument::loadHTML(); Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$emore .= "DOMDocument::loadHTML(); Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$emore .= "DOMDocument::loadHTML(); Fatal Error $error->code: ";
break;
}
$emore.=$error->message . ' at line ' . $error->line . ' <SUB>' . $error->line . '</SUB>, column ' . $error->column . ' <SUP>❓</SUP><br>';
if ($error->column == 0) {
$betterbws[-1 + $error->line]='<SUB>' . $error->line . '</SUB><SUP>❓</SUP>' . $betterbws[-1 + $error->line];
} else {
$betterbws[-1 + $error->line]=substr($betterbws[-1 + $error->line],0,$error->column) . '<SUB>' . $error->line . '</SUB><SUP>❓</SUP>' . substr($betterbws[-1 + $error->line],$error->column);
}
$betterwithin=str_replace($bws[-1 + $error->line], $betterbws[-1 + $error->line], $betterwithin);
}
libxml_clear_errors();
if ($emore != '') {
$emore=str_replace($within, str_replace('</SUP>','</sup>',str_replace('<SUP>','<sup>',str_replace('</SUB>','</sub>',str_replace('<SUB>','<sub>',str_replace('<BR>','<br>',str_replace('>','>',str_replace('<','<',$betterwithin))))))), $emore) . '</td></tr></table><br>';
}
$tec='No tidy module detected.' . $emore;
?>
โฆ be able to add to the precision of the HTML validation linking that right hand column entries with left hand HTML content context via the use of <sub>[line number]</sub> and <sup>&10067;</sup> () โlinkersโ
โฆ with thechanged tidy_examplephpโs proof
of concept PHP online HTML validator web application you can try here also, the mobile platform versions of which only allow the DOMDocument methodology be used.
Previous relevant HTML Online Validation via PHP Rendering Tutorial is shown below.
Onto yesterdayโs HTML Online Validation via PHP DOMDocument Tutorial, today, we tackle numerous improvements and fixes to our Online HTML Validator (PHP) web application as per โฆ
- add PHP tidy module parsing results in HTML report โฆ
<?php
$tec='' . tidy_error_count($tidy) . ' errors<br><pre onclick=woit(this); id=tidyout title="PHP tidy parses HTML to look like below and click here to show web browser rendering" style=background-color:pink;>' . str_replace('>','>',str_replace('<','<',tidy_get_output($tidy))) . '</pre>';
?> - add onclick=woit(this); logic to HTML content elements to show how the web browser would render such HTML โฆ
<?php echo โ
var wo=null;
var ofc=false;
var lasttitle=' ';
var lastcont='';
function woit(ine) {
var asttitle='';
var astcont='';
var rectis=ine.getBoundingClientRect();
if (wo) { wo.close(); wo=null; }
if (('' + ine.type) == 'textarea' && ofc) {
ofc=true;
} else {
if (('' + ine.outerHTML).indexOf('</h4>') != -1) {
if (('' + ine.getAttribute('data-render')) != '') {
var prefx='';
var sufx='';
if (('' + ine.getAttribute('data-render')).replace(/\>\;/g,'>').replace(/\<\;/g,'<').indexOf('</body>') == -1) {
prefx='<html><body>';
sufx='</body></html>';
} else if (('' + ine.getAttribute('data-render')).replace(/\>\;/g,'>').replace(/\<\;/g,'<').indexOf('</html>') == -1) {
prefx='<html>';
sufx='</html>';
}
astcont=prefx + ('' + ine.getAttribute('data-render')).replace(/\>\;/g,'>').replace(/\<\;/g,'<').replace(/\&\#10004\;\ /g,'').replace(/\&\#10060\;\ /g,'') + sufx;
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
wo=window.open('','_blank');
} else {
wo=window.open('','_blank','left=' + rectis.left + ',top=' + rectis.top + ',width=' + rectis.width + ',height=' + rectis.height);
}
wo.document.write(astcont);
wo.document.title='PHP DOMDocument parsing passed onto web browser rendering';
lastcont=astcont;
} else if (ine.innerHTML.indexOf('<br>' + String.fromCharCode(10) + '<br>' + String.fromCharCode(10) + '<br>' + String.fromCharCode(10)) != -1) {
astcont=ine.innerHTML.split('<br>' + String.fromCharCode(10) + '<br>' + String.fromCharCode(10) + '<br>' + String.fromCharCode(10))[1].replace(/\>\;/g,'>').replace(/\<\;/g,'<').replace(/\&\#10004\;\ /g,'').replace(/\&\#10060\;\ /g,'').replace(/\&\#10067\;\ /g,'');
asttitle='PHP DOMDocument parsing passed onto web browser rendering';
if (asttitle != lasttitle || astcont != lastcont) {
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
wo=window.open('','_blank');
} else {
wo=window.open('','_blank','left=' + rectis.left + ',top=' + rectis.top + ',width=' + rectis.width + ',height=' + rectis.height);
}
wo.document.write(astcont);
lastcont=astcont;
}
} else {
astcont=ine.innerHTML.replace(/\>\;/g,'>').replace(/\<\;/g,'<').replace(/\&\#10004\;\ /g,'').replace(/\&\#10060\;\ /g,'').replace(/\&\#10067\;\ /g,'').replace(/^\ \.\.\.\ /g,'');
asttitle='PHP DOMDocument parsing passed onto web browser rendering';
if (asttitle != lasttitle || astcont != lastcont) {
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
wo=window.open('','_blank');
} else {
wo=window.open('','_blank','left=' + rectis.left + ',top=' + rectis.top + ',width=' + rectis.width + ',height=' + rectis.height);
}
wo.document.write(astcont);
lastcont=astcont;
}
wo.document.title='PHP DOMDocument parsing passed onto web browser rendering';
lasttitle='PHP DOMDocument parsing passed onto web browser rendering';
}
} else if (('' + ine.type) != 'textarea') {
astcont=ine.outerHTML.split('</pre>')[0].split('>')[1].replace(/\>\;/g,'>').replace(/\<\;/g,'<');
asttitle='PHP tidy module parsing passed onto web browser rendering';
if (asttitle != lasttitle || astcont != lastcont) {
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
wo=window.open('','_blank');
} else {
wo=window.open('','_blank','left=' + rectis.left + ',top=' + rectis.top + ',width=' + rectis.width + ',height=' + rectis.height);
}
wo.document.write(astcont);
lastcont=astcont;
wo.document.title='PHP tidy module parsing passed onto web browser rendering';
lasttitle='PHP tidy module parsing passed onto web browser rendering';
}
} else {
astcont=ine.value;
if (document.URL.indexOf('localhost') != -1) {
asttitle='Your proposed HTML passed onto web browser rendering';
} else {
asttitle='Your proposed HTML passed onto web browser rendering';
}
if (asttitle != lasttitle || astcont != lastcont) {
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
wo=window.open('','_blank');
} else {
wo=window.open('','_blank','left=' + rectis.left + ',top=' + rectis.top + ',width=' + rectis.width + ',height=' + rectis.height);
}
wo.document.write(astcont);
lastcont=astcont;
if (document.URL.indexOf('localhost') != -1) {
wo.document.title='Your proposed HTML passed onto web browser rendering';
lasttitle='Your proposed HTML passed onto web browser rendering';
} else {
wo.document.title='Your proposed HTML passed onto web browser rendering';
lasttitle='Your proposed HTML passed onto web browser rendering';
}
}
}
}
}
โ; ?> - deal with user HTML that contains the + character (which PHP uses for space encoding, as distinct from Javascript encodeURIComponentโs use of %20 for space encoding) via new form onsubmit=โreturn butplus(this);โ logic as per โฆ
<?php echo โ
function butplus(thisf) {
var idof=thisf.innerHTML.split(' id=\"')[1].split('\"')[0];
if (document.getElementById(idof).value.indexOf('+') != -1) {
if (('' + thisf.method).toLowerCase() == 'get') {
location.href=thisf.action + '?' + idof + '=' + encodeURIComponent(document.getElementById(idof).value);
return false;
} else if (encodeURIComponent(document.getElementById(idof).value).length <= 800) {
thisf.method='GET';
location.href=thisf.action + '?' + idof + '=' + encodeURIComponent(document.getElementById(idof).value);
return false;
} else {
document.getElementById(idof).value=document.getElementById(idof).value.replace(/\+/g,encodeURIComponent('+'));
}
}
return true;
}
โ; ?> - add a
( ❓ ) status to DOMDocument parsing logic where the current interim okayed elementโs outerHTML is contained wholly in the previous erroneous parent element โฆ
<?php
function nodeContent($n, $outer=false) { // thanks to https://stackoverflow.com/questions/5404941/how-to-return-outer-html-of-domdocument
global $ioff, $origh, $lasth;
$d = new DOMDocument();
$b = $d->importNode($n->cloneNode(true),true);
$d->appendChild($b);
$h = $d->saveHTML();
// remove outter tags
if (!$outer) {
$h = substr($h,strpos($h,'>')+1,-(strlen($n->nodeName)+4));
} else {
if ($ioff == -2 && strpos(strtolower($h), '<html') !== false) { return ''; }
if ($ioff <= -1 && strpos(strtolower($h), '<body') !== false) { return ''; }
}
if (stripos( str_replace(' ','',str_replace(' ','',str_replace("\n",'',str_replace("\r",'',str_replace("\r\n",'',str_replace("'",'',str_replace('"','',$origh))))))),str_replace(' ','',str_replace(' ','',str_replace("\n",'',str_replace("\r",'',str_replace("\r\n",'',str_replace("'",'',str_replace('"','',('' . $h)))))))) ) !== false) {
if (strpos(str_replace('>','>',str_replace('<','<',str_replace(' ','',str_replace(' ','',str_replace("\n",'',str_replace("\r",'',str_replace("\r\n",'',str_replace("'",'',str_replace('"','',('' . $lasth)))))))))), str_replace('>','>',str_replace('<','<',str_replace(' ','',str_replace(' ','',str_replace("\n",'',str_replace("\r",'',str_replace("\r\n",'',str_replace("'",'',str_replace('"','',('' . $h))))))))))) !== false) {
return '❓ ' . $h;
}
$lasth="";
return '✔ ' . $h;
} else if (1 == 1) {
if (stripos($h, '</body>') === false) {
$lasth='❌ ' . $h;
return $lasth;
} else {
$lasth="";
return '❌ ' . $h;
}
}
return '' . $h;
}
?> - add more characters to ignore with the whitespace if checking codeline โฆ
<?php
if (stripos( str_replace(' ','',str_replace(' ','',str_replace("\n",'',str_replace("\r",'',str_replace("\r\n",'',str_replace("'",'',str_replace('"','',$origh))))))),str_replace(' ','',str_replace(' ','',str_replace("\n",'',str_replace("\r",'',str_replace("\r\n",'',str_replace("'",'',str_replace('"','',('' . $h)))))))) ) !== false) {
// here if parsed outerHTML matches input outerHTML
} else {
// here if parsed outerHTML does not match input outerHTML
}
?> - do some CSS styling with the input type=submit buttons โฆ
<?php echo โ
<style> input[type=submit] { width:40%; height:50px; border-radius:30px; } </style>
โ; ?>
Again, thechanged tidy_examplephpโs proof
of concept PHP online HTML validator web application you can try here also, the mobile platform versions of which only allow the DOMDocument methodology be used.
Previous relevant HTML Online Validation via PHP DOMDocument Tutorial is shown below.
The lack of platform functionality that yesterdayโs HTML Online Validation via PHP Tidy Primer Tutorial โIntranet feelingโ Online HTML Validator web application was supported by, got us interested in exploring HTML validation via PHP DOMDocument methodologies.
Yes, (serverside) PHP can work with (its version of) DOM (document object model) but not as well as (clientside) Javascript DOM can. The validation of HTML at the serverside (PHP) stage does not take into account web page rendering, and so struggles to detect โwell formed lookingโ HTML (but) with HTML tags that mean nothing. But we can do some validation checking via โฆ
<?php
function nodeContent($n, $outer=false) { // thanks to https://stackoverflow.com/questions/5404941/how-to-return-outer-html-of-domdocument
global $ioff, $origh;
$d = new DOMDocument();
$b = $d->importNode($n->cloneNode(true),true);
$d->appendChild($b);
$h = $d->saveHTML();
// remove outter tags
if (!$outer) {
$h = substr($h,strpos($h,'>')+1,-(strlen($n->nodeName)+4));
} else {
if ($ioff == -2 && strpos(strtolower($h), '<html') !== false) { return ''; }
if ($ioff <= -1 && strpos(strtolower($h), '<body') !== false) { return ''; }
}
if (stripos( str_replace(' ','',str_replace('"','',$origh)),str_replace(' ','',str_replace("\n",'',str_replace('"','',('' . $h)))) ) !== false) {
return '✔ ' . $h;
} else if (1 == 1) {
return '❌ ' . $h;
}
return '' . $h;
}
$doc = new DOMDocument();
$doc->loadHTML($htmlin);
foreach ($doc->getElementsByTagName("*") as $item) {
$elecnt++;
if ($rest == "") { $rest = " ... "; }
$rest.=' <br>' . str_replace('>','>',str_replace('<','<',nodeContent($item, true)));
}
$twc='Number of elements detected is ' . ($ioff + $elecnt) . $rest;
?>
Thechanged tidy_examplephpโs proof
of concept PHP online HTML validator web application you can try here also, the mobile platform versions of which only allow the DOMDocument methodology be used.
Previous relevant HTML Online Validation via PHP Tidy Primer Tutorial is shown below.
In terms of your, perhaps MAMP, Apache/PHP/MySql local web serverโs โlow hanging fruitโ for extending PHP functionality, what are the most straightforward PHP modules to look at? Weโd say โฆ
- find your Apache/PHP/MySql local web serverโs php.ini (or equivalent) โฆ
- (text) edit this php.ini โฆ
- find โextension=โ section and see those that are commented out โฆ
- these PHP modules will have been installed but held back from being activated โฆ
- you can then uncomment these โฆ
- File -> Save the new version of php.ini โฆ then โฆ
- stop and start your Apache/PHP/MySql local web server
โฆ and we found on our macOS Apache/PHP/MySql local web server the chance to introduce into our thinking, the use of the PHP module called โtidyโ. Doing the research and development on this lead us to believe we could develop an โonline HTML validatorโ (for small amounts of HTML on the RJM Programming domain version, initially), with tidy_examplephp (which weโd want you to download to the document root folder of your, perhaps MAMP, Apache/PHP/MySql local web server, to enable our โIntranetโ feeling modus operandi up at the RJM Programming public domain) proof
of concept PHP online HTML validator web application โฆ
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.