Error handling loading a URL - php

I'm using a library called Simple HTML DOM
One of it's methods, loads the url into a DOM object:
function load_file()
{
$args = func_get_args();
$this->load(call_user_func_array('file_get_contents', $args), true);
// Throw an error if we can't properly load the dom.
if (($error=error_get_last())!==null) {
$this->clear();
return false;
}
}
In order to test error handling, I created this code:
include_once 'simple_html_dom.php';
function getSimpleHtmlDomLoaded($url)
{
$html = false;
$count = 0;
while ($html === false && ($count < 10)) {
$html = new simple_html_dom();
$html->load_file($url);
if ($html === false) {
echo "Error loading url!\n";
sleep(5);
$count++;
}
}
return $html;
}
$url = "inexistent.html";
getSimpleHtmlDomLoaded($url);
The idea behind this code it's to try again if the url is failing to load, if after 10 attemps still fails, it should return false.
However it seems that with an inexistent url, the load_file method never returns false.
Instead I get the following warning message:
PHP Warning: file_get_contents(inexisten.html): failed to open stream
Any idea how to fix this?
Note: Preferably I would like to avoid hacking into the library.

Change your following code:
$html->load_file($url);
if ($html === false) {
for this one:
$ret = $html->load_file($url);
if ($ret === false) {
because you were checking object instance instead of the returned value from load_file() method.

By adding the # sign before a method call, any warnings get supressed. If you use this, always be sure to check for errors yourself as you do now and are sure no other methods are available to make sure no warnings and/or errors pop up.
You should check the actual data that is saved somewhere by the load() method if that equals FALSE instead of the object instance $html.

Related

Create Permission Without Checking URI Segment on Codeigniter

i've a function from my simple library and called on some controllers
$activeUrl = str_replace(base_url(),"",current_url());
erporate_acl::has_permission($activeUrl);
and then here is my library code, here the code try to matching current URI ($param) data from database :
public static function has_permission($param){
$CI =& get_instance();
$CI->load->model('acl_model');
$user = $CI->session->userdata('user');
$arrPerms = $CI->acl_model->permissions($user);
$currentMeth = strtolower(str_replace("::", "/", $param));
$result = "";
if (!empty($arrPerms))
{
if (strpos($currentMeth,'edit') !== false || strpos($currentMeth,'view') !== false || strpos($currentMeth,'delete') !== false) {
$str = preg_replace('#\/[^/]*$#', '', $currentMeth);
$result = in_array($str, $arrPerms);
}else{
$result = in_array($currentMeth, $arrPerms);
}
}else{
$result = false;
}
if ($result == false) {
show_error("<font size='+1'>Sorry, You Don't Allowed to Access !</font><br><a href='".#$_SERVER['HTTP_REFERER']."'>Back to Previous Page</a>");
}
}
the point of my script above is matching in_array if in_array return false then we got error message, else allowed to access the page
Is it possible to create permission without checking URI segment ?
Thank you and sorry for my bad English
You can use the following:
$this->router->class to get the controller class that is being used;
$this->router->method to get the method being called;
$this->router->directory to get the directory in case you have sub-folders in your url being used.
This way you don't really need to check the url that's being used. You can just check the controller and method.
This is an agnostic way from the routes. In case you have something like:
users/get/1
user/marco-monteiro
ACL based on the uri segment can be hard in these situations. Using the methods I mentioned it gets easier.

better way to avoid call to a member function

I need to know if there is a better way to avoid Call to a member function xxxx() on null
currently I'm coding as follows but it is cumbersome.
if($event->getForm()
&& $event->getForm()->getParent()
&& $event->getForm()->getParent()->getParent()
&& $event->getForm()->getParent()->getParent()->getData()
&& $event->getForm()->getParent()->getParent()->getData()->getComponente()
){
$componente = $event->getForm()->getParent()->getParent()->getData()->getComponente();
$formModifier($event->getForm(), $componente, $defaultComponente);
}
In PHP 7 this is actually a catchable Error (if you're using hhvm it's a regular Exception):
try {
$componente = $event->getForm()->getParent()->getParent()->getData()->getComponente();
} catch (\Error $e) {
$componente = null;
}
if ($componente !== null) {
$formModifier($event->getForm(), $componente, $defaultComponente);
}
In PHP 5 there is a workaround using intermediate variables and the and keyword instead of &&:
if (
$f = $event->getForm() and
$p = $f->getParent() and
$p2 = $p->getParent() and
$d = $p2->getData() and
$componente = $d->getComponente()
) {
$formModifier($f, $componente, $defaultComponente);
}
If you use && instead of and you'll get "undefined variable" notices and this workaround won't work.
Working examples: https://3v4l.org/0S6ps
no there is no way, but at least you can do some performance improvement
$form = $event->getForm();
if(!$form){
//do error handling
return;
}
$parent = $form->getParent();
if(!$parent){
//do error handling
return;
}
$p_parent = $parent->getParent();
if(!$p_parent){
//do error handling
return;
}
$data = $p_parent->getData();
if(!$data){
//do error handling
return;
}
$component = $data->getComponente();
...
this way you call each function only once and you can do better error handling
I think this is a great example of a bad code. By having a code like this you're breaking several rules and making your life much harder than it should be.
Your code is rigid, fragile, hard to understand and maintain etc.
Simpler is ALWAYS better.
If you can't make your $xx->getComponent() a proper object easily accessible without such ugly nested relationship, you should at least encapsulate the method into something appropriate and use that instead, so if anything changes, you don't have to go full mental and change it all over the place.
This class seems strange in it's creation, but if you are not extracting these methods dynamically using __call(), you can use method_exists() in a loop inside a function, something similar to:
function getMethodChain($class,$arr = ['getForm','getParent','getParent','getData','getComponente'])
{
# First check the object is set
if(!is_object($class))
return false;
# Loop intended method chain
foreach($arr as $method) {
# Check if the method exists in the current class or passed already
$useClass = (!isset($classPass))? $class : $classPass;
# Check if the method exists in the current class
if(is_object($useClass) && method_exists($useClass,$method)) {
# Assign this class/method to use next in the loop
$classPass = $useClass->{$method}();
}
else
return false;
}
# Just send back
return (isset($classPass))? $classPass : false;
}
The use would be something like:
# This will either be the data you expect or false
$componente = getMethodChain($event);

Invalid argument supplied for foreach() simple_html_dom.php on line 1119

here is what im doing
im using
pthreads from - pthreads.org
php Simple Html DOM parser from - simplehtmldom.sourceforge.net
now the process of what i (will) do is:
I am reading a bulk of links that is from a text file.
I initialize a thread to have a separate process
I am creating a log file for this thread so that i will know, what happens later on.
now, this is my code for my thread class.
class ReadLinks extends Thread {
private $conn;
private $links;
private $fileObj;
public function __construct($conn, $links, $fileObj) {
//.. well just asign this to the global variables
}
public function run() {
try {
$this->logMsg("Start Reading Reviews");
$this->readLinks();
} catch (Exception $ex) {
$this->logMsg($ex);
}
$this->closeLog();
}
private function readLinks() {
$this->logMsg("Links");
foreach ($this->links as $link) {
$link = trim(preg_replace('/\s\s+/', ' ', $link));
$this->logMsg("Link: " . $link);
$html = html_readLink($link);
break;
}
}
private function logMsg($msg) {//something to write on the text file
}
private function closeLog() {//closes the textfile
}}
$conn - is my mysqli link to have db actions in the future
$links - is an array of links to be read.
$fileObj- is a resource return from fopen(). ( well to write into a file)
now who is that html_readlink,
its an outer function that is like this:
function html_readLink($link) {
return file_get_html($link);}
basically it is the resource returned by a function from simple html dom parser
now, i have as well a function that reads a link alone to do the other (different business requirement) and im using the simple html dom parser with ease.
with the pthreads, i tried writing the file(logs first) so that i can ensure that everything as well works fine.
about contacting db is not yet sure., well ill try to figure it out if it works fine, but logically it should work.
now when i called this class: its like this:
try {
$thread = new readLinks($conn, $Links, createlog());
if ($thread->start()) {
$thread->join();
} else {
echo "something i need to research if this happens";
}
} catch (Exception $err) {
echo $err; //something i need to research as well if this happens
}
i got this error
Warning: Invalid argument supplied for foreach() in C:\my\path\to\simplehtmldom_1_5\simple_html_dom.php on line 1119
that simplehtmldom code is:
function clear()
{
foreach ($this->nodes as $n) {$n->clear(); $n = null;}
// This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
if (isset($this->children)) foreach ($this->children as $n) {$n->clear(); $n = null;}
if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);}
if (isset($this->root)) {$this->root->clear(); unset($this->root);}
unset($this->doc);
unset($this->noise);
}
now that is the source code coming from simple html dom. that foreach is the one that is returning the error. now my other code using simple html dom doesn't have a problem with simple html dom. but with pthreads i got this error.
also, when i change my codes and didn't use pthreads, (had some revisions like this:
on pthreads class:
class ReadLinks {// extends Thread {
//insert other codes
public function readLinks() {
$this->logMsg("Links");
foreach ($this->links as $link) {
$link = trim(preg_replace('/\s\s+/', ' ', $link));
$this->logMsg("Link: " . $link);
$html = html_readLink($link);
$this->logMsg(getTitle($html));
//
break;
}
}
and change the way this is called like this:
try {
$thread = new ReadLinks($conn, $revLinks, createlog());
$thread->readLinks();
// if ($thread->start()) {
// $thread->join();
// } else {
// echo "something i need to research if this happens";
// }
} catch (Exception $err) {
echo $err; //something i need to debug and research if this happens
}
everything works fine, i get the desired results.
pthreads is something i need to use since loading bulk links and reading each of them is quite a time consuming process. and i need it to be on a separate thread. now i dont know whats wrong with these pthreads, or simple html dom parser. have i done something unnecessary/wrong? is there other way to do this?
anyone??
EDIT
i followed the answer of Prafulla Kumar Sahu:
the new code for the function clear() of simple html dom is:
function clear() {
if (is_array($this->nodes) || $this->nodes instanceof Traversable) {
foreach ($this->nodes as $n) {
$n->clear();
$n = null;
}
}
// This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
if (isset($this->children))
foreach ($this->children as $n) {
$n->clear();
$n = null;
}
if (isset($this->parent)) {
$this->parent->clear();
unset($this->parent);
}
if (isset($this->root)) {
$this->root->clear();
unset($this->root);
}
unset($this->doc);
unset($this->noise);
}
the result is: it eliminated the error
but it is not the desired result
when using the function
$x=$resource->find($selector,0);
//resource is the return obj of file_gets_content, selector is my css selector string
it returns null/empty where in fact it should have a value.
ive checked a separate function that uses the simple html dom after i updated their code, seems it wasn't affected, and it is working properly. but with my pthread class, it is not working correctly.
The code I have doesn't have a foreach on line 1119; maybe you have an older version. You're getting a warning only, what problem(s) do you see in the results?
1117 // save dom as string
1118 function save($filepath='')
1119 {
1120 $ret = $this->root->innertext();
1121 if ($filepath!=='') file_put_contents($filepath, $ret, LOCK_EX);
1122 return $ret;
1123 }
It happens if the variable you are trying to traverse using foreach is not irritable so please check if your variable is either an array or instanceof Traversable class .
*It may be because you are not getting any value for that variable you want to traverse.
so, I would suggest you to use is_array( $whatever ) || $whatever instanceof Traversable just before foreach.
ie.
if( is_array( $whatever ) || $whatever instanceof Traversable ){
foreach( $whatever as $what ){
//some code
}
}
In your case it is
function clear()
{
foreach ($this->nodes as $n) {$n->clear(); $n = null;}
// This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
if (isset($this->children)) foreach ($this->children as $n) {$n->clear(); $n = null;}
if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);}
if (isset($this->root)) {$this->root->clear(); unset($this->root);}
unset($this->doc);
unset($this->noise);
}
source:- https://github.com/jalbertbowden/simplehtmldom/blob/master/simplehtmldom_1_5/simple_html_dom.php#L1119
this means you are unable to get $this->nodes correctly, so please var_dump it before you are calling function clear or before the foreach .

Error using PHP Simple HTML DOM Parser in Symfony

Here is a basic example of how simple_html_dom works in a standalone php file.
test.php:
include ('simple_html_dom.php');
$url = "http://www.google.com";
$html = new simple_html_dom();
$html->load_file($url);
print $html;
If I execute it with the command: php test.php
It dumps correctly the html of the website (in this example, google.com)
Now let's take a look at a basic example of code using a Symfony task:
class parserBasic extends sfBaseTask {
public function configure()
{
$this->namespace = 'parser';
$this->name = 'basic';
}
public function execute($arguments = array(), $options = array())
{
$url = "http://www.google.com";
$html = new simple_html_dom();
$html->load_file($url);
print $html;
}
}
This file it's located under: <appname>/lib/task
I don't need to include the library in the file because being under the lib/task folder, it gets automatically loaded.
I execute the task using this command: php symfony parser:basic
And I get the following error message:
PHP Fatal error:
Call to a member function innertext() on a non-object in
/home/<username>/<appname>/lib/task/simple_html_dom.php on line 1688
Any suggestions?
The problem comes from Symfony.
In fact, if an error occur when loading the file with simple_html_dom, it won't say anything but returning false.
For example, if you perform this in your task:
$url = "http://www.google.com";
$html = new simple_html_dom();
$res = $html->load_file($url);
if (false === $res)
{
throw new Exception("load_file failed.");
}
print $html;
You will get an exception. If you tweak simple_html_dom to display en error when loading a file, around line 1085:
// load html from file
function load_file()
{
$args = func_get_args();
$this->load(call_user_func_array('file_get_contents', $args), true);
// Throw an error if we can't properly load the dom.
if (($error=error_get_last())!==null) {
// I added this line to see any errors
var_dump($error);
$this->clear();
return false;
}
}
You will see:
array(4) {
["type"]=>
int(8)
["message"]=>
string(79) "ob_end_flush(): failed to delete and flush buffer. No buffer to delete or flush"
["file"]=>
string(62) "/home/.../symfony.1.4/lib/command/sfCommandApplication.class.php"
["line"]=>
int(541)
}
I usually got this error (which is, in fact, a notice) when using task. The problem is here, in sfCommandApplication, with ob_end_flush:
/**
* Fixes php behavior if using cgi php.
*
* #see http://www.sitepoint.com/article/php-command-line-1/3
*/
protected function fixCgi()
{
// handle output buffering
#ob_end_flush();
ob_implicit_flush(true);
To fix that, I comment the line with #ob_end_flush();. And every thing goes fine. I know, it's an ugly fix, but it works. An other way to fix that, is to disable notice from PHP (in php.ini), like :
// Report all errors except E_NOTICE
error_reporting = E_ALL ^ E_NOTICE

Check if an xml is loaded with simplexml_load_string

I am querying an xml file with php like this :
public function trackOrderAction()
{
$request = Mage::getResourceModel( 'order/request' );
$request->setOrder($this->getRequest()->getParam('increment_id'));
$response = $request->submit(true);
$xml = simplexml_load_string($response);
$items = count($xml->OrderItems->OrderItem);
}
The xml is not ready immediately so if people try to use the function before it is ready there is an error because it is trying to get the property of a non-object. My question is what is the proper way to check the xml response to see if there is anything and stop the function if there is not?
I tried something simple like
if (empty($xml)){
die();
} else {
$items = count($xml->OrderItems->OrderItem);
}
But this does not help. Any ideas on how to check to see if the xml loaded?
From http://us.php.net/manual/en/function.simplexml-load-string.php
Returns an object of class SimpleXMLElement with properties containing
the data held within the xml document. On errors, it will return
FALSE.
public function trackOrderAction()
{
$request = Mage::getResourceModel( 'order/request' );
$request->setOrder($this->getRequest()->getParam('increment_id'));
$response = $request->submit(true);
$xml = simplexml_load_string($response);
if ( !$xml ) {
return false;
}
$items = count($xml->OrderItems->OrderItem);
}
It will return false if there was an error. So fail right away if simplexml_load_string fails and return false. Otherwise continue on with the rest of the function. Never die() in a function.

Categories