I was wondering if someone can show me a good way to handle errors in my PHP app, that i am also easily able to reuse in my other scripts.
So far i have been using the following functions:
Inline Errors
function display_errors_for($fieldname) {
global $errors;
if (isset($errors[$fieldname]))
{
return '<label for="' .$fieldname. '" class="error">' . ucfirst($errors[$fieldname]). '</label>';
} else {
return false;
}
}
All Errrors
function display_all_errors($showCounter = true) {
global $errors;
$counter = 0;
foreach ($errors as $errorFieldName => $errorText)
{
if ($showCounter == true)
{
$counter++;
echo '<li>' . $counter . ' - <label for="' .$errorFieldName. '">' .$errorText. '</label></li>';
} else {
echo '<li><label for="' .$errorFieldName. '">' .$errorText. '</label></li>';
}
}
}
I have a $errors = array(); defined on the top of my global file, so it is appended to all files.
The way i use it is that, if i encounter an error i push a new error key/value to the $errors array holder, something like the following:
if (strlen($username) < 3) {
$errors['username'] = "usernames cannot be less then 3 characters.";
}
This all works great and all, But i was wondering if some one has a better approach for this? with classes? i don't think i want to use Exceptions with try/catch seems like an overkill to me.
I'm planning to make a new app, and i'll be getting my hands wet with OOP alot, though i have already made apps using OOP but this time i'm planning to go even deeper and use OOP approach more extensively.
What i have in mind is something like this, though its just a basic class i will add further detail to it as i go deeper and deeper in my app to see what it needs.
class Errors
{
public $errors = array();
public function __construct()
{
// Initialize Default Values
// Initialize Methods
}
public function __destruct()
{
//nothing here too...
}
public function add($errorField, $errorDesc)
{
if (!is_string($errorField)) return false;
if (!is_string($errorDesc)) return false;
$this->errors[$errorField] = $errorDesc;
}
public function display($errorsArray)
{
// code to iterate through the array and display all errors.
}
}
Please share your thoughts, if this is a good way to make a reusable class to store and display errors for an entire app, or is getting more familiar with exceptions and try/catch my only choice?
I cannot imagine a reason for creating a distinct class for as trivial task as echoing several text strings into browser.
Or I am missing something.
I see no point for the function even.
Personally I have in my templates (where I need it) the code block like this
<? if ($data['errors']): ?>
<div class="errors">
<ul>
<? foreach ($data['errors'] as $e): ?>
<li><?=$e?></li>
<? endforeach ?>
</ul>
</div>
<? endif ?>
and find it quite enough
If you are into OOP, you should use framework like Zend Framework. In ZF there is an action helper for general error/result messages: FlashMessenger
http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelpers.flashmessenger
Related
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 .
Until now, unless I made a multilingual website (where I would use .mo & .po files), all the text would be scrambled all around the template and / or class files. Instead, I would like to store all static text in a file that is easily editable by my coworkers and clients (that rules out database storage and POedit).
I made a JSON file that stores the messages / static text like this:
{
"titles": {
"main_title": "This is the main title of the website",
"login_page_title": "Please, sing in",
"about_page_title": "About us"
},
"errors": {
"empty_required_field": "This field is required.",
"database_connection_error": "Couldn't connect to the database.",
}
}
Then I import it in the index.php file:
$messages = json_decode(file_get_contents("messages.json"));
And use it like:
echo($messages->titles->main_title);
Which has been working so far so good (although I'm uncertain that there aren't better ways to archieve this). At least in the template pages where everything is html with minimal logic.
But I'm having trouble using the strings from the JSON file inside the classes' functions. I would like to use the error messages when throwing exceptions, for example. But I'm quite reluctant about stating "global $message" in every function where it's used (feels repetitive). Also everybody says that globals are naughty.
So my questions are two:
1) Is the JSON file a good way to handle my problem? (and if not, why, and which method would be better?).
2) How could I retrieve the stored strings from inside the classes? I'm thinking something like extending the Exception class to include the error messages, but I'm unsure of how to do it.
Thanks in advance for your help.
One approach, which Laravel takes, is creating some sort of directory tree like the following:
lang/
en/
titles.php
errors.php
titles.php could contain the following:
<?php
return [
'main_title' => 'This is the main title of the website',
'login_page_title' => 'Please, sing in',
'about_page_title' => 'About us'
];
As for errors.php:
<?php
return [
'empty_required_field' => 'This field is required.',
'database_connection_error' => "Couldn't connect to the database.",
];
I don't really like the JSON approach because it's not very flexible. For one, in PHP files, you have access to any variables you may want to give it, there's comments, possibility of using functions to create some messages, etc. This is why I recommend the above method.
In order to get the messages, you would require the file in a variable, like $titles = require 'lang/en/titles.php', using it like: $titles['main_title']. This method also makes it easy to change the language if needed.
While I'm not 100% sure I understand your exception problem, you would throw an exception with the appropriate message like: throw new Exception($errors['empty_required_field']);
In the end I opted for a Singleton class that loads/includes a separate text file. Nice global scope and should be easy to adapt to other needs (multilingüal, separate language files, or whatever). As I said I'm no expert so all critique is welcome.
<?php
class CustomText {
private static $instance = null;
private static $text;
private function __clone() {}
// On construct, checks if the strings are stored in a session.
// If not, retrieves them from file and stores them in a session.
private function __construct() {
if(self::isStoredInSession() == true) {
self::$text = $_SESSION["custom_text"];
} else {
//self::$text = json_decode(file_get_contents("messages.json"),true);
self::$text = include_once("messages.php");
self::saveToSession();
}
}
// Private initialization called on every public method so I don't have to worry about it on other files.
private static function initialize() {
if(self::$instance == null) {
self::$instance = new self;
}
}
// Session management
private static function saveToSession() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION["custom_text"])) {
$_SESSION["custom_text"] = self::$text;
}
}
private static function isStoredInSession() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(isset($_SESSION["custom_text"])) {
return true;
}
return false;
}
// Sample public functions
public static function getText($section,$string){
self::initialize();
if(isset(self::$text[$section][$string])) {
return self::$text[$section][$string];
} else {
return "";
}
}
public static function getError($string) {
self::initialize();
if(isset(self::$text["error"][$string])) {
return self::$text["error"][$string];
} else {
return "";
}
}
public static function getWebsiteTitle($section,$divider = " - ") {
self::initialize();
$title = "";
if(isset(self::$text["title"]["main"])) {
$title .= self::$text["title"]["main"];
}
if(isset(self::$text["title"][$section])) {
if(!empty($title)) {
$title .= $divider;
}
$title .= self::$text["title"][$section];
}
return $title;
}
}
What worries me the most is that I'm not sure that storing the data in a session is better that including a file on each page, and I have everything twice in the session variable and the class parameter.
For a long time I have a problem - should I reuse small parts of code and if so, how should I do it so it would be the best practice.
What I mean about small code is for example:
if (!is_array($table)) {
$table = array($table);
}
or
$x = explode("\n", $file_content);
$lines = array();
for ($i=0, $c = count($x); $i<$c; ++$i) {
$x[$i] = trim($x[$i]);
if ($x[$i] == '') {
continue;
}
$lines[] = $x[$i];
}
Such tiny parts of code may be used in many classes in one project but some of them are used also in many projects.
There are many possible solutions I think:
create simple function file and put them all reusable piece of codes as function, include them simple in project and use them whenever I want
create traits for those piece of codes and use them in classes
reuse code by simple copy paste or creating function in specific class (??)
other ?
I think all of those solutions have their pros and cons.
Question: What method should I use (if any) to reuse such code and why is this approach the best one in your opinion?
I think that "the best way" depends on many factors including the technology your applications use (procedural, OOP), versions of PHP they run on, etc. For example, traits are interesting and useful but they are available only since php 5.4.0 so using this tool to group your code snippets you will not be able to reuse them in systems running on earlier PHP versions. On the other hand if your app uses an OOP style and you organized your resuable small code snippets in functions, their usage may seem awkward in an OOP app and conflict with the function names in a particular class. In this case I think grouping your functions in classes would seem more natural.
Putting everything together, it seems that classes provide better tool for grouping resuable code snippets in terms outline above, namely backward compatibility with earlier PHP versions, avoiding function names conflicts, etc.) Personally I code mostly in OOP, so i have a Util class where I group small functions representing resuable pieces of code snippets that do not directly relate to each other and thus could not be logically groupped in other classes.
As mentioned already traits are a good thing. But might be a bit hard to manage after a while, and it might not be supported everywhere since its new.
What I do is to create Tool classes that have a lot small static functions, like:
class ArrayTools
{
static public function CheckArray($array)
{
if (!is_array($array))
{
$array = array($array);
}
return $array;
}
}
So you can call it with ArrayTools::CheckArray($array)
Please go with traits if your code mainly involves classes and objects.. As the the concept of traits exclusively focusses on code reuse ability.
Following are the code snippets which actually I use with Plain PHP projects, these code snippets are used from various frameworks good traits and best practices.
1.
The following code is used to check the environment in which your working, based on the environment you can set the some global variables, error reporting as so on.
if(!defined('ENVIRONMENT')){
define('ENVIRONMENT','DEVELOPMENT');
}
if (defined('ENVIRONMENT'))
{
switch (ENVIRONMENT)
{
case 'DEVELOPMENT':
case 'TESTING':
$base_url = 'http://localhost/project_name/';
error_reporting(E_ALL);
break;
case 'PRODUCTION':
$base_url = 'http://hostname/project_name/';
error_reporting(0);
break;
default:
exit('The application environment is not set correctly.');
}
}
2.
/* This function is used to PRINT the ARRAY data in the pre formatted manner */
if (!function_exists('pr')) {
function pr($data) {
echo '<pre>', print_r($data), '</pre>';
}
}
3.
/* This function is used to Sanitize the user data and make data safe to insert into the database */
function sanitize($data) {
global $link;
$data = trim($data);
return htmlentities(strip_tags(mysqli_real_escape_string($link, $data)));
}
4.
/* Used to get the difference of 2 arrays
Returns the array with difference
*/
function multi_diff($arr1,$arr2){
$result = array();
foreach ($arr1 as $k=>$v){
if(!isset($arr2[$k])){
$result[$k] = $v;
} else {
if(is_array($v) && is_array($arr2[$k])){
$diff = multi_diff($v, $arr2[$k]);
if(!empty($diff))
$result[$k] = $diff;
}
}
}
return $result;
}
5.
/* This fnction is used to generate the random keys of specific length
Accepts parameter of certain length if not specified it will generate 20 bit length automatically
*/
function generate_random_key($length = 20) {
//Initializing the varialble
$keystring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
$random_key = '';
for ($i = 0; $i < $length; $i++) {
$random_key.=$keystring[rand(0, strlen($keystring) - 1)];
}
//Return the randomly generated key
return $random_key;
}
6.
/* This function outputs the errors in ul>li format with unstyled
* To get the bullets styling remove class='list-unstyled' in <ul> tag */
function output_errors($errors){
$output = array();
foreach ($errors as $error) {
$output[] = '<li>'.$error.'</li>';
}
return '<ul class="list-unstyled">'.implode('', $output).'</ul>';
}
7.
/* Checks whether the user is loggedin else will redirect to the protectect page */
function protected_page(){
if(is_loggedin() === false){
// header('Location: protected.php');
header('Location: logout.php');
exit();
}
}
8.
/* If user tries to access the page directly accessing through the URL,
* If already loggedin then redirect him to any of the inner page
*/
function login_redirect(){
if(is_loggedin() === true){
header('Location: home.php');
}
}
9.
/* This function is used to check whether the user exists or not */
function email_exists($email){
/* Your Code */
}
/* This function is used to check whether the user isActive or not */
function is_active($email){
/* Your Code */
}
/* This function will get the userid from the email */
function userid_from_email($email) {
/* Your Code */
}
/* This fucntion is used to login the user based on the email-id and password */
function login($email,$password){
/* Your Code */
}
/* Check whether the USER is loggedin or not */
function is_loggedin(){
return (isset($_SESSION['userid'])) ? true : false;
}
Hope this helps you. Cheers!
How is the best way to show my content in architecture MVC? Actually I'm doing the following way:
<?php
class view {
function __construct(){
}
function __set($var,$value){
$this->var = $value;
}
function __get($var){
return $this->var;
}
function render($render, $noinclude = false){
if($noinclude == true){
require ("view/template/".$render.".php");
}
else{
require ("view/template/headerTPL.html");
require ("view/template/bannerTPL.html");
require ("view/template/menuTPL.html");
require ("view/template/".$render.".php");
require ("view/template/footerTPL.html");
}
}
function show($value){
$this->value = $value;
}
function alert($value){
echo "<script>alert('{$value}')</script>";
}
}
?>
<div id="conteudo">
<?php require $this->menu; ?>
</div>
This is my render.php /\
Is it wrong to include the TPL? How should I do it?
Your separating your HTML code into template files is on track, and a good way to do things. I see some HTML at the bottom of this file. I would recommend you move that into a template as well.
Just as a general guideline, the main logic of your program (controller) should never have any HTML created from it. It should pass the data (model) to the template (view) in an pure and unformatted state, then the template code will format it properly.
If you want to speed up the development of MVC programs, I would recommend you check out the CodeIgniter framework. http://codeigniter.com/
Any advice is welcome!
I have a very limited understanding of php classes but below is my starting point for the route I would like to take. The code is a reflection of what I see in my head and how I would like to go about business. Does my code even look ok, or am I way off base?
What are your thoughts, how would you go about achieving such a task as form->validate->insertquery->sendmail->return messages and errors?
Please try and keep your answers simple enough for me to digest as for me its about understanding whats going on and not just a copy/paste job.
Kindest regards,
Phil.
Note: This is a base structure only, no complete code added.
<?php
//=======================================
//class.logging.php
//========================================
class logging
{
public $data = array();
public $errors = array();
function __construct()
{
array_pop($_POST);
$this->data =($this->_logging)? is_isset(filterStr($_POST) : '';
foreach($this->data as $key=> $value)
{
$this->data[$key] = $value;
}
//print_r($this->data); de-bugging
}
public function is_isset($str)
{
if(isset($str)) ? true: false;
}
public function filterStr($str)
{
return preg_match(do somthing, $str);
}
public function validate_post()
{
try
{
if(!is_numeric($data['cardID'])) ? throw new Exception('CardID must be numeric!') : continue;
}
catch (Exception $e)
{
return $errors = $e->getCode();
}
}
public function showErrors()
{
foreach($errors as $error => $err)
{
print('<div class="notok"></div><br />');
}
}
public function insertQ()
{
$query = "";
}
}
//=======================================
//Usercp.php
//========================================
if(isset($_GET['mode']))
{
$mode = $_GET['mode'];
}
else
{
$mode = 'usercp';
}
switch($mode)
{
case 'usercp':
echo 'Welcome to the User Control Panel';
break;
case 'logging':
require_once 'class.logging.php';
$logger = new logging();
if(isset($_POST['submit'])
{
if($logger->validate_post === true)
{
$logger->insertQ();
require_once '/scripts/PHPMailer/class.phpmailer.php';
$mailer = new PHPMailer();
$mailer->PHPMailer();
}
else
{
echo ''.$logger->showErrors.'';
}
}
else
{
echo
'
<form action="'.$_SERVER['PHP_SELF'].'?mode=logging" method="post">
</form>
';
}
break;
case 'user_logout':
// do somthing
break;
case 'user_settings':
// do somthing
break;
?>
I have decided to use this method for returning errors rather than print them in the method, thanks for the advice Igor!
catch (Exception $e)
{
$this->errors[] = $e->getMessage();
#ERROR DE_BUGGING ONLY================================
#print('<pre>');
#print_r($this->errors);
#print('</pre>');
#=====================================================
}
if($this->errors)
{
return false;
}
else
{
return true;
}
It looks like you have a decent understanding of OOP code. I see declared public vars and even try/catches, though I'd say don't forget the "public" visibility keyword in front of "function __construct()"—not absolutely necessary, but it keeps with good coding practices.
Further, I would say that everything you are doing here has been written, debugged, and fixed, and proven production worthy already by each of the dozens of PHP frameworks out there. The specific task you mentioned, "form->validate->insertquery->sendmail->return messages and errors" is so incredibly easy with Zend Framework, my framework of choice. And I would imagine the same is true for Symphony, Solar, Cake, etc.
Do yourself a favor and stop coding what has been coded already. Learn a framework that has a community, regular updates, and well-written thorough documentation. Again, I recommend Zend Framework.
First advice that comes to mind: Separate logic from presentation. You can start by using some template engine like Smarty. If you will keep it all mixed up, soon it will be a huge dump.
Also try to include class definitions from separate files, and as a next step I would recommend adopting some pattern like Model-View-Controller to separate models from logic.
That's what I can think of without digging too deep into the code.