Coding Horrors: PHP Exception Model
PHP 5 introduced structured exception handling(try..catch) into its codebase a while ago
and alongside it a new exception model, similar to those you'd find in other programming
languages.
I've been using trycatches for many years in other languages like C#, Vb.net, Delphi, javascript and
a few others, but find it a bit lacking in PHP. The whole idea is to wrap errors and throw an
exception object in its place, which we can catch and handle gracefully.
Something that you can't count on with an out of the box PHP distro(maybe in PHP 6?), some functions
and classes do this, some don't, others do it half heartedly (looking at you SimpleXML) - you essentially
end up needing to cater for two exception models (the old and the new).
Consider the following code snippet:
try { $handle = file_get_contents("somefile.txt"); } catch(Exception $ex) { echo "Something went wrong"; }
If somefile.txt doesn't exist, one would expect that the message "Something went wrong" would be displayed. Instead we find that our try..catch gets ignored (since its looking for a non existing exception object) and we end up with an ugly message like this:
Warning: file_get_contents(somefile.txt) [<a href='function.file-get-contents'>function.file-get-contents</a>]: failed to open stream: No such file or directory in C:\xampp\htdocs\public\exc\index.php on line 120
Luckily there is a workaround to this issue; PHP allows us to set a custom error handler via the set_error_handler function, from there we can return the exception objects our try..catch blocks desires.
function error_handler($errno, $errstr, $errfile, $errline) { throw new Exception($errstr, $errno); } set_error_handler('error_handler');
The set_error_handler supports bit-flags, so if you need to exclude any type of error to be handled like this, simply do like the following:
set_error_handler('error_handler', E_ALL^E_NOTICE);
Hooray now we've got everything throwing exceptions, provided that we include this code at the top of every page, but we're still not completely happy.
Its all good and well that we're getting an exception object, but we've got no idea what type of exceptions are being thrown. Ideally we'd like to have the following:
try { $contents = file_get_contents("somefile.txt"); } catch(IOException $ex) { echo "Something went wrong with the file"; } catch(Exception $ex) { echo $ex; }
Achieving this would require quite a bit more work, since majority of PHP functions simply throw the default exception type. Which means we need to write custom exceptions classes and the means to trigger them.
Instead of calling file_get_contents directly like in the example, one would create a wrapper class for handling files, which would throw custom exceptions. Alternatively one could attempt to parse error messages and throw exceptions via the global error handler (not all that great).
Have a look at the following example of what one can do, firstly our custom exception class (IOException) and then our wrapper class (File). Once we invoke File::ReadAllText("somefile.txt"), we receive an IOException, instead of the default exception type.
Notice the @ sign infront of the file_get_contents function, this tells PHP to suppress errors and not to display them. Also notice $php_errormsg, this is a variable available within scope of the current executing code, containing the last triggered error.
Note that $php_errormsg only gets created if "track_errors" is set to on in your configuration file or if we ini_set("track_errors","on") it before our executing code.
class IOException extends Exception { public $fileName; } class File { public static $fileName; private static function throwException($php_errormsg) { $customException = new IOException($php_errormsg); $customException->fileName = self::$fileName; throw $customException; } public static function ReadAllText($fileName) { self::$fileName = $fileName; return @file_get_contents($fileName) or self::throwException($php_errormsg); } }
Obviously one should always try to avoid exceptions as far as possible and handle issues even before they happen. Certain exceptions still require very specific ways of how they might need to be handled however.
2009-08-15: Regarding the point I made here about creating different exception classes according to the type of exception.
The current way PHP implements exceptions (one exception to rule them all), one can fall into the trap where you swallow all exceptions - including the unforseen ones - that should have been unhandled and logically terminate execution of your script. (Since its unforseen, it can open your script up to all kinds of security issues, other issues etc)
Hence one would need specific exception types and avoid a catch-all and swallow all situation.
Date - 2008-09-16 05:31:23
Comments - 2
Design-time support for ASP.net composite controls
Creating composite controls in ASP.net can prove to be quite
a tedious process at times, when it comes to the actual HTML the control renders.
Especially since design-time support for composite controls are quite limited.
Consider the following piece of HTML from the
demo code, how would one go to work to render this from a control?
<table> <tr> <td> <input name=" txtUrl" type="text" id=" txtUrl" style="width:470px;" value="http://" /> </td> <td> <input type="submit" name="btnDisplay" value="Go" id=" btnDisplay" /> </td> </tr> <tr> <td colspan="2"> <iframe id="ifBrowser" width="500px" height="500px"></iframe> </td> </tr> </table>
If we look at the following crude examples.
We can override the render method and make use of the methods exposed by the HtmlTextWriter parameter, like in example 1.
One might even consider to override the CreateChildControls method, and adding the elements to the controls collection, like in example 2.
Example 1
protected Button btnDisplay = new Button(); protected TextBox txtUrl = new TextBox(); protected HtmlGenericControl ifBrowser = new HtmlGenericControl("iframe"); protected override void Render(HtmlTextWriter writer) { writer.WriteFullBeginTag("table"); writer.WriteFullBeginTag("tr"); writer.WriteFullBeginTag("td"); txtUrl.ID = "txtUrl"; txtUrl.Text = "http://"; txtUrl.Width = new Unit(470); txtUrl.RenderControl(writer); writer.WriteEndTag("td"); writer.WriteFullBeginTag("td"); btnDisplay.ID = "btnDisplay"; btnDisplay.Text = "Go"; btnDisplay.CommandName = "Click"; btnDisplay.RenderControl(writer); writer.WriteEndTag("td"); writer.WriteEndTag("tr"); writer.WriteFullBeginTag("tr"); writer.WriteBeginTag("td"); writer.WriteAttribute("colspan", "2"); writer.Write(">"); ifBrowser.ID = "ifBrowser"; ifBrowser.Attributes["width"] = "500px"; ifBrowser.Attributes["height"] = "500px"; ifBrowser.RenderControl(writer); writer.WriteEndTag("td"); writer.WriteEndTag("tr"); writer.WriteEndTag("table"); base.Render(writer); }
Example 2
protected Button btnDisplay = new Button(); protected TextBox txtUrl = new TextBox(); protected HtmlGenericControl ifBrowser = new HtmlGenericControl("iframe"); protected override void CreateChildControls() { HtmlTable mainTable = new HtmlTable(); HtmlTableRow firstRow = new HtmlTableRow(); HtmlTableRow secondRow = new HtmlTableRow(); mainTable.Controls.Add(firstRow); mainTable.Controls.Add(secondRow); HtmlTableCell firstRowFirstCell = new HtmlTableCell(); txtUrl.ID = "txtUrl"; txtUrl.Text = "http://"; txtUrl.Width = new Unit(470); firstRowFirstCell.Controls.Add(txtUrl); HtmlTableCell firstRowSecondCell = new HtmlTableCell(); btnDisplay.ID = "btnDisplay"; btnDisplay.Text = "Go"; btnDisplay.CommandName = "Click"; firstRowSecondCell.Controls.Add(btnDisplay); firstRow.Controls.Add(firstRowFirstCell); firstRow.Controls.Add(firstRowSecondCell); HtmlTableCell secondRowfirstCell = new HtmlTableCell(); ifBrowser.Attributes["width"] = "500px"; ifBrowser.Attributes["height"] = "500px"; ifBrowser.ID = "ifBrowser"; secondRowfirstCell.Controls.Add(ifBrowser); secondRowfirstCell.ColSpan = 2; secondRow.Controls.Add(secondRowfirstCell); this.Controls.Add(mainTable); base.CreateChildControls(); }
I've never liked any of these approaches, it just feels like a very "unnatural" way to represent HTML.
It is however possible to add some real design-time support of our own. My initial thoughts were that one would be able to simply embed an user control (ascx) as a web resource inside an assembly and simply use it like that. Which would be a logical choice, since user controls have full design-time support for authoring.
It wasn't a simple case of embed and off you go, I ran into a few difficulties, but came up with a very simple solution.
Have a look at the following abstract class (we're only going to use this as a base class for embedded user controls).
public abstract class EmbeddedUserControl : CompositeControl { protected Control UserControls; public T Control<T>(string id) where T : Control { this.EnsureChildControls(); return (T)UserControls.FindControl(id); } protected override void CreateChildControls() { Assembly assembly = Assembly.GetExecutingAssembly(); string name = this.GetType().Namespace + "." + this.GetType().Name + ".ascx"; using (StreamReader sr = new StreamReader(assembly.GetManifestResourceStream(name))) { string content = sr.ReadToEnd(); UserControls = (DesignMode) ? new LiteralControl(content) : Page.ParseControl(content); } this.Controls.Add(UserControls); base.CreateChildControls(); } }
What happens here is we override the CreateChildControls method, and parse an embedded ascx file, which will act as our designer.
You will also notice a generic method, it simply functions as wrapper, and ensures that the controls collection are fully populated, before we try to use them.
Creating a new control will work like this:
Step 1: Add an blank ascx file to your control library (you will notice there isn't an option for ascx in the list (if you did it right, hint control library project :P), choose text file and just name it eg browser.ascx) This will allow you to drag & drop controls onto the page, and work with all the nice little visual tools in VS.
Step 2: ENSURE THAT THE BUILT ACTION OF THE ASCX PAGE IS SET AS "Embedded Resource"
Step 3: Add a class and inherit from the EmbeddedUserControl class, GIVE IT THE SAME NAME AS WHAT YOU GAVE THE ASCX FILE eg browser.cs, obviously not the same extension thought ;)
Step 4: Code away.
For the purpose of this post, I created a composite control that acts like a little browser window. Which you can download here.
Date - 2008-08-29 03:38:23
Comments - 0
First 16 17 18 19 20 21 22 23 24 25 Last / 42 Pages (83 Entries)