Debugging PHP: Advanced Techniques

Debugging PHP: Advanced Techniques

with Jon Peck

 


Debug your PHP code efficiently with a variety of client and server-side tools. In this concise course for experienced PHP developers, author Jon Peck demonstrates how to leverage PHP's built-in tools, as well as the Xdebug and Firebug extensions, and FirePHP libraries to improve the quality of your code and reduce troubleshooting overhead.

This course covers installation of Xdebug on Apache as well as working with the NetBeans IDE (integrated development environment). Jon then introduces native web browser developer tools for Firefox and Chrome, and demonstrates browser independent web debugging tools. Best practices for debugging and profiling web application failures and performance issues are also covered.
Topics include:
  • Configuring PHP error reporting
  • Logging errors to file
  • Gracefully handling fatal errors
  • Installing Xdebug
  • Understanding the principles of remote debugging
  • Remote debugging with NetBeans
  • Extending your browser with Firebug, FirePHP, or ChromePHP

show more

author
Jon Peck
subject
Developer, Programming Languages, Web Development
software
PHP , Xdebug
level
Intermediate
duration
1h 29m
released
Dec 17, 2012

Share this course

Ready to join? subscribe


Keep up with news, tips, and latest courses.

submit Course details submit clicked more info

Please wait...

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.



Introduction
Welcome
00:00(music playing)
00:04Hi! I'm John Peck, and welcome to Debugging PHP Advanced Techniques.
00:08In this course we'll look at how to debug your PHP code effectively
00:12using a variety of client and server-side tools.
00:14I'll start by showing you how to configure PHP to handle
00:18and report warnings, notices and errors.
00:20Next, I'll introduce Xdebug, including installation and configuration,
00:24then demonstrate how to best use Xdebug.
00:26We'll be covering many tools and techniques, including how to debug applications
00:31using multiple browsers.
00:33Finally, best practices for debugging and profiling web application failures
00:37and performance issues will be covered.
00:39The next time an issue needs to be addressed, a full toolkit of options will be available.
00:44Now, let's get started with Debugging PHP Advanced Techniques.
Collapse this transcript
Using the exercise files
00:00In this course I'll be developing in a Sandbox PHP environment.
00:04The recommended configuration for the host environment is PHP 5.3 or above
00:09and Apache 2 or above.
00:10Other Web servers such as Nginx or IIS will not be covered in this course.
00:15Setting up a Web server stack is beyond the scope of this course.
00:18If you do not already have a development server,
00:22I recommend using a local development server running on your workstation.
00:24The Up and Running with Linux for PHP Developers course here in
00:28the lynda.com Online Training Library will allow you to have an optimized virtual server
00:32running like any other program in your existing operating system.
00:35I will be demonstrating using a server created using this technique.
00:39Alternatively, you can use a Web server solution stack package
00:42in your native operating system.
00:43XAMPP from apachefriends.org has distributions for Linux, Windows,
00:49Mac OS X, and Solaris.
00:51WampServer from wampserver.com is explicitly for Windows and MAMP
00:57from mamp.info is for Mac OS X only.
00:59Each of these packages will allow you to execute the exercise files found in this course.
01:03Installing additional software within your native operating system is covered
01:07in the course, Installing Apache, MySQL and PHP with David Gassner, here in the
01:12lynda.com Online Training Library.
01:15Regardless of the location of your Web server, you will need
01:18access to the commands line with administrative credentials in order to install
01:21and configure server software.
01:23For Mac and Linux, the terminal allows you to access the command line,
01:27which includes access to the SSH command if the site is hosted remotely.
01:31For Windows, you can use the free program PuTTY, to connect via SSH to
01:35remote servers available from the official PuTTY website.
01:38I will be demonstrating writing code and debugging interactions using the
01:42NetBeans 7.2 IDE bundle for PHP.
01:45NetBeans is a free, open-source and cross-platform integrated development
01:49environment from netbeans.org that also supports the Xdebug PHP extension.
01:54The goal of the course is to teach debugging techniques, not how to use NetBeans.
01:58I recognize that different IDEs implement Xdebug interactions in different ways,
02:03but the base principles remain constant across some implementations.
02:06The exercise files for this course are contained in folders by chapter and movie.
02:11On my workstation, I have them in a folder named sandbox that my
02:15virtualized Linux server can access.
02:17Depending on your web server configuration, you may need to store these files
02:20in a different place, such as a remote Web server or in a folder accessible
02:24by a local Apache and PHP stack.
02:26Included in the exercise files are a number of free PHP libraries,
02:30including ChromePHP, FirePHPCore, PHP_Debug and Webgrind.
02:36No configuration is necessary and usage will be described during the course.
02:39A final note, as different Web hosts and configurations serve content
02:44from different URLs, the address you see in my browser may not exactly match
02:49what you see on your workstation.
02:50Additionally, the location shown in the command prompt demonstrations
02:53will differ depending on the location of site files and the configuration of your server.
02:57The software configuration will be very similar, if not identical across platforms.
Collapse this transcript
What you should know
00:00Before starting this course, you should have a basic knowledge
00:03of the PHP language and have written a few scripts.
00:06Without this background, you may lack the context necessary to understand
00:09what you are doing, which will not serve you well.
00:12If you are looking for some background or a refresher, I recommend
00:16PHP with MySQL Essential Training with Kevin Skoglund here in the
00:20lynda.com Online Training Library.
00:22A basic understanding of object-oriented programming in PHP is also assumed
00:26in this course, as debugging problems encountered when interacting
00:29with objects will be covered.
00:31If you would like some more background, see Object-Oriented Programming with PHP
00:35here in the lynda.com Online Training Library.
00:38Finally, if you are unfamiliar with configuring Web server components,
00:42check out the course Installing Apache, MySQL and PHP with David Gassner here in the
00:47lynda.com Online Training Library for a comprehensive perspective on how to best
00:51manage the solution stack.
Collapse this transcript
1. Built-In PHP Debugging
Exploring PHP error levels and consequences
00:00Many years ago, the computer I shared with my family had a program that executed on login
00:04that would randomly display a funny phrase or joke.
00:08One of them is stuck with me.
00:10I did it! I found the program's last bug, bug, bug, bug, bug, bug, bug.
00:14It worked on multiple levels, the joy of finding a problem
00:17and the humorous weirdness of a problem manifesting within the error message itself.
00:21As a software developer, I write computer programs to perform specific tasks
00:25in order to solve problems.
00:27When I write programs in PHP, I'm conforming to the specifications and rules
00:31of both the language and the limitations of the server environment so the
00:35solutions need to be within those boundaries.
00:37What happens when I bend or break these rules?
00:40An error or a mistake will cause the program to behave erratically or fail.
00:45So what is debugging?
00:47In its purest sense, debugging is the act of identifying and removing errors
00:51from a computer program.
00:52PHP provides a number of built-in mechanisms for debugging scripts by detecting
00:56and reporting these errors.
00:58In order to understand these reports, some perspective into why they are created
01:02and what they mean is necessary.
01:04I'll start by describing how PHP works.
01:07PHP is an interpreted language, which means scripts are indirectly executed
01:11by an interpreting program.
01:13There are a number of advantages to this approach, including
01:16platform flexibility, smaller program size, and dynamic typing, where values
01:21have types and variables do not.
01:23Some of the drawbacks of an interpreted language are that,
01:26in addition to decreased efficiency, some kinds of errors cannot be detected
01:30until that specific piece of the program is executed.
01:33This is in contrast to compiled programs, where the language code is converted
01:37into machine code, and the compilation of the entire program is halted
01:41when a bad enough error is detected.
01:43In computer programming, there are two distinct kinds of errors.
01:47A syntax error occurs when there is a violation of the programming language's
01:51set of rules that define the possible combination of symbols in correctly-structured programs.
01:56In short, the source code broke the rules of the language.
02:00Typically, execution or compilation is halted and cannot proceed.
02:05This abnormal termination is colloquially referred to as a crash.
02:10In contrast, illogical error, also known as a semantic error, occurs when
02:14the program operates incorrectly, but was structured correctly within the rules of the language.
02:19Rather than breaking the rules, the problem is a failure of purpose
02:23and unexpected behavior is the result.
02:26PHP provides a number of low-level mechanisms for detecting and reporting problems
02:30with the execution of a script.
02:32By providing a standardized structure, developers have a clear expectation
02:36of how the program will perform in exceptional conditions,
02:40like what conditions will cause a crash.
02:42These problems are separated into a hierarchy of error reporting levels
02:47where there are distinctions made about the severity and consequences of the error,
02:50along with when the problem occurs.
02:53There are three distinct severities of problems in PHP, from least to most severe.
02:58A notice is a non-fatal advisory that while execution worked,
03:03there is a good probability that there is room for improvement.
03:06A warning is also non-fatal, but is more severe.
03:09A warning indicates that there's a strong possibility that errors will occur in the future.
03:13And an error is fatal, meaning either the script cannot be interpreted
03:17or during execution the program exceeds its boundaries and was terminated.
03:22Some referred to these crashes as the "white screen of death," as often
03:26fatal errors occur before renderable content is sent to the browser,
03:30so the browser shows nothing, but a blank page.
03:33There are two distinct times when problems occur.
03:36Compile time, which is generated by the PHP parser when the script is
03:40interpreted before it can be executed; and run time, when
03:44the script is interpreted correctly by the parser and the problem occurs during execution.
03:49Given the context of the severity and timing, I can describe
03:52how PHP reports these problems.
03:54As of PHP 5.4, there are 16 distinct error reporting levels that encompass
03:59these problem severities in times.
04:02Each level has a constant name and integer value.
04:05Generally, developers have no practical need to know the integer values,
04:09as the error levels should only be referred to in code by their constant name.
04:13When configuring error reporting, the integer values of these error levels
04:17are combined using a bit mask and the resulting integer is used
04:20as a bit field by the interpreter.
04:22I'm going to describe the most common error levels.
04:25Later, I will provide examples of each, practically demonstrating
04:29how each of these error levels are triggered and show you how to resolve them.
04:32E_ERROR is a fatal run-time error that cannot be recovered from,
04:37halting script execution and resulting in a crash.
04:40Examples include running out of memory.
04:42E_WARNING, which is a run-time warning, which means a nonfatal error
04:47that does not halt execution of the script.
04:49E_PARSE, which is a compile-time parse error generated by the PHP parser,
04:54which prevents script execution from even starting.
04:57E_NOTICE, which is a run-time notice, meaning that there is a possibility
05:02that an error occurred or that everything could be fine and the script executed normally.
05:06In practice, run-time notices typically come from logical errors in the script.
05:10E_STRICT, which is a compile-time notice where PHP has detected
05:15a forward compatibility issue in the code.
05:17Introduced in PHP 5.0, E_STRICT is useful for detecting older sloppy programming
05:22and adhering to coding standards.
05:23E_DEPRECATED, which is a run-time notice that warns that code in the script
05:28will not work in future versions of PHP.
05:31And finally, E_ALL, which is a combination of all errors, warnings and notices.
05:35There is a small exception;
05:37E_STRICT was not introduced in E_ALL until PHP 5.4.
05:42Given the context of what these error levels actually mean, I'll demonstrate
05:46how to control how PHP reports problems.
Collapse this transcript
Configuring PHP to report what went wrong
00:00In PHP there are two configuration options that control what errors are reported
00:05and whether these errors are displayed.
00:06Depending on the platform or software distribution that is being used,
00:10the initial configuration of your system may be different from the default,
00:14so I'll describe how to both get and set these options.
00:17Error reporting sets the error reporting level with an integer representing
00:21a bit field normally through a named constant.
00:24By default, PHP reports everything except E_NOTICE and for versions
00:29prior to PHP 5.4, also excluding E_STRICT.
00:33Display_errors determines whether or not to display errors to the user.
00:36It's either 1 (on) or 0 (off).
00:40By default, display_errors is set to 1 (on), meaning errors are shown to the user.
00:44Display_errors should only be set to 1 (on) for development servers
00:48and never shown to users, as this both confuses unskilled users
00:52and provides context for malicious users.
00:54Error logging will be covered in-depth in the next segment.
00:57There are four places where error_reporting and display_errors can be set
01:01in order of increasing priority and granularity.
01:04PHP.ini, which is the PHP configuration file, which requires
01:09a server restart and affects the entire server;
01:14httpd.conf, this is the Apache server configuration file and editing requires a server restart as well.
01:18It also requires to allow override options or allow override all privileges;
01:23htaccess, which are directory level configuration files for PHP does not require
01:29a server restart, but isn't always available and also requires allow override options
01:33or allow override all privileges.
01:36And finally, run time, which is in the script itself and it's always available.
01:41Now depending on the level of administrative access available,
01:45it may be possible that the only option available is run time configuration.
01:49However, there are some configuration options that cannot be changed at run time,
01:52so access to php.ini is preferred.
01:56I will demonstrate how to edit php.ini and to set these options at run time,
02:01but not the other configuration files out of interest of focusing on PHP
02:05and not Apache server configuration.
02:07The first method I will demonstrate is editing php.ini, which will arbitrarily
02:12set the configuration for every site using that Web server.
02:15On a development server, this is optimal and easy, but this technique
02:19may not be appropriate in all circumstances.
02:21Use your best judgment.
02:23To set error_report and display_errors in php.ini, I need to first
02:27locate the configuration file.
02:29Similar to how defaults maybe different across distributions and platforms,
02:33the configuration file location can be found in a number of different places.
02:37Some solution stacks such as MAMP actually generate the php.ini file,
02:42so consult the documentation if the following technique is not effective.
02:45The easiest way to determine the location of the configuration file is
02:49using the function phpinfo() which outputs information on the PHP configuration.
02:54I'm going to switch to my IDE NetBeans and open the demo.php file
03:00from the exercise files.
03:01I'll add one line, phpinfo, save, then go to the browser and navigate
03:08to where the test script is.
03:09In my case, it's http.//sandbox.dev.8080.
03:16A large amount of debugging information is shown.
03:18I'm looking for loaded configuration file, which contains the complete path
03:22of the php.ini file.
03:24In my environment, I can see that the path is set to etcphp5apache2php.ini.
03:32Now that I know where the file is, I can edit it.
03:34As I'm using a virtualized development server, I'm going to SSH into it
03:38then edit the file.
03:40If you are not using a virtualized server or you are using a hosted server
03:43or solution stack, then the method used to edit the file and restart the Apache Web server
03:47will be slightly different, but the contents of the file will be very similar.
03:52First, I'll open a console then I will SSH into the development environment.
03:58Next, I will edit the configuration file shown by phpinfo using administrative
04:03credentials, sudo nano -w and then the file.
04:09I will Search for the line containing error_reporting =.
04:15This is one of the lines that I edited in the Up and Running with Linux for PHP Developers,
04:18where I set the configuration to every single notice, warning,
04:23and error and added strict notices as well.
04:25Above this line are a few suggested configurations for different environments,
04:29including the default value and production, if not already set,
04:33change the error_reporting to E_ALL | E_STRICT.
04:36Next, Search for display_errors, display_errors =, by default it's set to on,
04:44if not, change it to on.
04:46Exit by pressing Ctrl X and press Y if you changed anything.
04:50Finally, if there are changes that need to take place, I will need to restart
04:54the Apache Web server in order to allow the new PHP configuration options to be read.
04:59This will differ from server to server, but the most common way to do this
05:03and the way used for the virtualized development server is to issue the following command,
05:06sudo /etc/init.d/apache2restart.
05:13Now, I'll go back to the IDE.
05:16As I now know where the configuration setting is, I no longer need the phpinfo function call,
05:20so I'll remove it.
05:22If you were unable to locate the configuration file or do not have permission to change it,
05:26I will now demonstrate how to set these values at run time in the script itself.
05:31A caveat, if display_errors is set at run time, it won't have any effect
05:36if the script has a fatal error before execution, as the desired run time action does not get executed.
05:41Additionally, if you are using a third-party host, they may have disabled the use
05:45of the function I will be demonstrating for security reasons.
05:48If so, find an alternative such as a local development server that you directly administer.
05:53First, I'm going to set the error_reporting level using the function error_reporting,
05:56which takes one parameter, the integer value for the bit mask,
06:01which I will use constants to create.
06:03I'll use the same value as I did in php.ini.
06:06I'll start with the comment, // Set error_ reporting, error_reporting (E_ALL | E_STRICT).
06:18There are two functions that I will use to interact with PHP options, ini get and ini set.
06:24Ini get returns the value currently stored for a given configuration option,
06:28echo <h1>display_errors: before</h1>
06:33and var_dump (ini_get('display_errors'));.
06:37
06:42Next, I'm going to intentionally trigger a notice by attempting to access
06:46as a variable that does not exist, echo <h1>Triggering notice</h1>;
06:52then var_dump ($error);
06:58which does not exist.
06:59I'm going to explicitly disable the display of errors using the function ini_set
07:04to override the php.ini option.
07:07Ini_set takes two parameters, the name of the option to be set and the new value.
07:11So echo<h1>Setting errors</h1>;
07:15then ini_set ('display_errors', 0).
07:24I'll verify the new setting has been saved;
07:28I can copy and paste this code and then attempt to trigger the notice again.
07:35Save, then return to the browser and refresh.
07:40The first display_errors shows that it was set to 1 by the php.ini.
07:45When I triggered a notice by accessing error, the undefined variable,
07:49it is displayed to the screen followed by the result of var_dump,
07:52which correctly returned to null.
07:53Next, the ini_set function call, which does not display anything when called.
07:58The value for display_errors is now set to 0 and therefore the attempt
08:02to trigger a notice fails.
08:03Let's simplify the file a bit.
08:05We'll continue not to display errors.
08:06Do not display_errors.
08:13Finally, to reiterate a point from earlier, using ini_set to control display_errors
08:17will only work if the script actually executes.
08:21If there is a fatal error, the default value set in php.ini will be used instead.
08:26So if I just do error, this is an invalid line, save then return
08:31to the browser and refresh.
08:34Despite attempting to disable error display, a parse syntax error is displayed,
08:39because the run time action was not executed.
08:41Keep this in mind when configuring production servers in preparing code for production.
08:45I am going to switch back to the IDE and clean up the demo file to prepare
08:50for the rest of the course.
08:51So I'm going to Display_errors and then I'm going to intentionally
08:57echo <h1>Triggering notice</h1>; var_dump ($error);
09:04and save.
09:05Now that I've set error_reporting to a development finding value
09:09and I'm displaying errors to the screen, I'll explore how to log errors to a file for analysis.
Collapse this transcript
Logging errors to file
00:00There are a number of configuration options relating to logging errors, but only a couple
00:04are really necessary for most scenarios.
00:07Similar to error_reporting options, these options are set in php.ini
00:12and can be overridden using ini set.
00:14Log_errors, which defaults to off (0), to not log errors.
00:17I'm going to set log_errors to on to log_errors.
00:20Use of 0 and 1 will also work as values, which I'll need to use with ini set.
00:25Log_errors_max_len is the length of the error messages stored
00:29in the logs measured in bytes.
00:30The default is 1024 bytes.
00:33If messages are getting truncated, I can either set it to a higher value
00:37or set it to 0 for unlimited message length.
00:39Error_log, which sets the name of the file where the script errors should be logged.
00:43by default, it's Null and empty.
00:46If log_errors is on and error log is null, errors will be sent to the Apache
00:50error log from the Web server or standard error from a command line interface.
00:54These three configuration options give me the ability to enable error_logging.
00:58As a best practice, PHP error should not be included in the Apache error logs
01:03as they're really separate contexts.
01:05Additionally, the error_log should be set to somewhere outside the Web root,
01:09such as var/log/apache2/php.
01:12For demonstration purposes, I'm going to set the error_log to a local file
01:15in the sandbox folder.
01:17Finally, if space is at a premium and the log files are getting very large,
01:21set a logical value to the log_errors maximum length.
01:24First, I'll demonstrate how to edit php.ini.
01:29Sudo nano -w /etc/php5/apache2/php.ini.
01:38Search for log_errors =.
01:42If it's not a ready set, change it to log_errors = On.
01:46Move down a couple of lines to log_errors' max length.
01:49For debugging purposes, it's helpful to remove the limit of the size
01:52of the error_logs, so set it to 0.
01:55Finally, Search for error_log.
01:58I'm going to explicitly set the log file location to within the sandbox folder,
02:02which in my case is media/sf_sandbox/php_errors.log.
02:08Press Ctrl+X to Exit, and if prompted, Y to save the changes.
02:12If there are changes, restart the Apache Web server,
02:15sudo/etc/init.d/apache2 restart.
02:21I'm going to change directory to the shared folder, cd/media/sf_sandbox/.
02:26And start watching the error log using the tail command to follow changes to php_errors.log.
02:32Touch php_errors.log, then follow the file, tail -f php_errors.log.
02:41Open the browser and reload the page.
02:45The notice for the undefined variable is shown on the page and switching back
02:50to the log is shown in the logs as well.
02:53Next, I will demonstrate how to specify these configuration options within a PHP script.
02:57Remember, this will only work if the script can be parsed, so if there's a fatal error,
03:02the customization will not work.
03:03I'm going to switch back to the IDE, then at the top, I'll use ini_set again.
03:09Instead of off and on, I'll use the integer value in this context.
03:12First, I'll turn on error logging.
03:14So log errors, ini_set('log_errors') and we'll set it to 1.
03:24Then I'll remove the limit from the length, No error log message max,
03:32ini_set('log_errors_max len', 0).
03:37And then I'll specify the location of the error log.
03:42I'm going to put it in the same directory as the demo script using a relative path
03:46and I'll name it to something slightly different to demonstrate
03:49the precedence of where configuration options are set.
03:51Specify log_file, ini_set ('error_log', and I'm going to set it to the
03:58relative path to the current directory and error_log.txt, save, then return
04:04to the browser and reload.
04:08Nothing changes, but if I look at the terminal, I don't see the notice from the terminal.
04:14Cancel the tail by pressing Ctrl+C, then cat error_log.txt.
04:20The notice will be shown, which means the local ini set takes precedence over the php.ini.
04:26Now that I've demonstrated how to turn on php_error logging to a file,
04:30I'm going to cleanup the demo file a little bit.
04:32Switching back to the IDE, let's specify the same error_log file name
04:36as I set in php.ini, media/sf_sandbox/php_errors.log.
04:47Remove the other test code and save the file.
04:50Next, I'm going to demonstrate how to intentionally trigger a number of common
04:54php_errors and how to avoid the problems in the first place.
Collapse this transcript
Triggering and repairing PHP errors
00:00I'm going to demonstrate a number of different kinds of errors then repair each of them.
00:04As I will be removing each test code when it's fixed, I will provide the code
00:08in a separate exercise file.
00:10The first kind of error I will demonstrate is E_ERROR.
00:13E_ERROR example where I will intentionally run out of memory.
00:20This is a fatal run time error that cannot be recovered from,
00:24halting script execution and resulting in a crash.
00:26I'll start by setting the memory limit to 1 kilobyte, ini_set ('memory_limit', '1K');
00:35then I'll create a giant object by casting an array containing the numbers of 0 to 1,000
00:40as an object, var_dump ((object) range (0, 1000)), save then go to the browser and refresh.
00:51The fatal error will be displayed indicating that the system ran out of memory.
00:54I'll return to the IDE and remove the artificially low memory limit, save, then try again.
01:04This time the contents of the object are rendered correctly and the script does not crash.
01:08Depending on the context of a script, it may make sense to increase the memory limit
01:12of PHP but coordinate with any system administrators before making changes
01:16that can potentially consume more server resources than were expected.
01:20Back to the IDE, erase the E_ERROR code.
01:24I'm going to generate another fatal error, this time an E_PARSE error for bad syntax,
01:28E_PARSE error - bad syntax.
01:33I'm going to leave off the trailing semicolon, which is going to cause a parse error,
01:37echo 'fail', save the file.
01:40NetBeans is going to mark this line as being an error and underlines it,
01:44and if I hover my cursor, I'll see that this is a syntax error.
01:47Go to the browser and refresh.
01:51A fatal syntax error has been triggered and explicitly states that the semicolon is missing.
01:55In this particular case, it's an easy fix.
01:58I'll switch back and just add a semicolon.
02:01Next, I'll create another real-world parse error, unmatched brackets.
02:06So if (True) { save and refresh.
02:10This time I see the same unexpected end, but the error says nothing about the missing bracket.
02:15This is often a problem, especially when refactoring code with multiple
02:18nested if statements.
02:20The use of coding standard help avoid this kind of problem by enforcing
02:23spacing and indentation.
02:25To fix, go back to the IDE and just add the missing bracket.
02:29I'm going to remove the code and start over, because I'm going to create an E_WARNING,
02:34a nonfatal problem.
02:37Previously I discussed how ini_set may not be available on some third-party hosts.
02:41For demonstration purposes, I'm going to recreate that problem,
02:44which will trigger an E_WARNING for all the ini_sets in the demo.
02:48From the terminal, edit php.ini again, sudo nano -w/etc/php/5/apache2/php.ini.
03:01Search for disable_functions = and add ini_set to the end of the list, ini_set,
03:10press Ctl X to Exit, Y to Save and restart then Web server,
03:14sudo /etc/init.d/apache2 restart.
03:21Go back to the browser and refresh.
03:24The only thing triggering a warning is the disallowed function,
03:27which in this case is just generating a warning.
03:29The solution in this case is to either not use ini_set or allow the use of it again.
03:34Return to the terminal, edit php.ini and remove the ini_set from the disable functions directive.
03:42Ctrl X and Y to Save.
03:46Save, then restart the Web server, return to the browser, and refresh.
03:51The error messages are gone.
03:53Going back to the IDE, I'm going to demonstrate another nonfatal error level, E_NOTICE.
04:00I typically see notices when I try to do something goofy, like access
04:03a variable, value or property that does not exist.
04:06E_NOTICE - typically accessing something undefined.
04:12First, I'll dump array which doesn't exist, var_dump($array);
04:17Next, I'll define array but access a key that isn't set.
04:21So $array = array ();
04:25var_dump($array[0]);.
04:27Finally, I'll create an empty class and access a property that doesn't exist
04:31named property, $object = new std(Class();
04:37and then var_dump($object ->property);
04:43save then return to the browser and refresh.
04:47Three notices are displayed, one for the undefined variable,
04:51a second for the undefined array offset, and a final one for the undefined object property.
04:56To repair; only attempt to access things that exist, there's a couple of ways
05:00of dealing with this gracefully.
05:01Return to the IDE, the best solution for the undefined variable is to define the variable.
05:07However, when attempting to access offsets or properties, it is inappropriate
05:11to just set the element in question. So what should I do?
05:14Use the isset function, which does not trigger any errors of the thing being
05:18checked does not exist.
05:19So in this case, if (isset($array [0])) { then var_dump.
05:26This is also good logic, as if I'm trying to access something and it doesn't exist,
05:29that it probably is not a good thing and additional action should be taken.
05:34The same isset technique works for objects as well.
05:37So, if (isset($object->property)) {, save and then return to the browser and refresh.
05:47Instead of a number of notices, the empty array is displayed.
05:51Two more error levels remain.
05:53Back to the IDE, I'm going to remove the previous test content.
05:57I'm going to start with E_STRICT, which enforces best practices
06:02and is intended to prevent sloppy programming.
06:04One of the most common ways to trigger an E_STRICT warning is treating a method
06:09as static that isn't E_STRICT - mixing scopes.
06:14I'll create a simple class called strict with one function named trigger,
06:18which will just display the word triggered, class Strict { function trigger () { echo 'triggered';
06:27Without instantiating strict, I'll call trigger, Strict::trigger();
06:33save, then return to the browser and refresh.
06:37I get a rather verbose notice telling me not to mix my scopes by statically
06:41calling a nonstatic method.
06:42Back to the IDE. This is a quick fix, make the function static.
06:47Alternatively, I could instantiate strict then instead of using the scope
06:51resolution operator, I could call the function normally.
06:54If I didn't make it static, just do $strict = new Strict ();
07:02and then $strict->trigger();
07:05Either way use your best judgment given the context.
07:08Save and return to the browser and refresh.
07:12Triggered is shown and none of notices are.
07:15Back to the IDE and removing the old content, I'll demonstrate the final error level,
07:19which is E_DEPRECATED, which is shown when I try to use something
07:23that is no longer supported and will be removed, E_DEPRECATED - something will be removed soon.
07:30I'll demonstrate using two deprecated techniques.
07:33The first is creating a new object by reference.
07:37So $deprecated = & stdClass();
07:42then var_dump($deprecated);
07:45Next is the split function.
07:49I see these both in legacy PHP 4 code, var_dump(split(' , ', one, two, three))
08:00save then return to the browser and refresh.
08:04Both worked, but both also trigger E_DEPRECATED notices.
08:08The repairs are straightforward in this case.
08:10In the IDE, remove the ampersand sign as PHP 5 creates objects correctly.
08:16The split function was replaced with explode which takes the same parameters.
08:21Save the changes and test them in the browser.
08:25Both function in the same way just without the notices.
08:28I have one more way of triggering errors to demonstrate manually triggering an error,
08:32which can be useful for logging custom errors.
08:35The trigger_error function takes two parameters, the string to be displayed
08:39and an optional integer with the error level.
08:42Now these error levels are different than the ones I have demonstrated,
08:45these are user error levels.
08:47There are three of them.
08:47E_USER_NOTICE, which is the default level of trigger error, E_USER_WARNING
08:54and E_USER_ERROR. Go back to the IDE and remove the test code.
09:00I'm going to manually trigger each of the error levels
09:03trigger_error {'Custom notice', which I will use the constant for E_USER_NOTICE);
09:11and trigger_error{'Custom warning', E_USER_WARNING);
09:19and then trigger_error{ 'Custom error', E_USER_ERROR);
09:25I'm going to add a final line which will never be reached due to the triggering
09:30of the E_USER_ERROR, echo 'will not execute';
09:34save and then go back to the browser and refresh.
09:39The notice, warning and error shown in the final echoed 'will not execute'
09:43is not shown, as the fatal error was triggered and shutdown PHP.
09:47In the next segment, I will demonstrate how to gracefully handle fatal errors.
Collapse this transcript
Gracefully handling fatal errors
00:00While developers strive to write fault tolerant and problem-free code,
00:04inevitably fatal errors and crashes do occur.
00:07Rather than just presenting a user with a blank screen or interrupted output,
00:11it's possible to programmatically execute a function just before
00:15a PHP completely shutsdown using the PHP function register_shutdown_function.
00:20As these functions execute during shutdown, typically there is little context
00:24as PHP has already been cleaning up.
00:26I've heard this described as writing a note after falling from a cliff.
00:30However, the function error_get_last will still work in this context
00:34and will provide the line, file, error type and message as an associative array.
00:39I'm going to demonstrate how to register a custom shutdown function
00:42that will notify both the user and the administrator of the problem, but with the caveat of
00:46this will not work if the file containing the register shutdown function has a parse error.
00:51In the IDE, I'm going to start by registering the shutdown function
00:55just after the block of ini_sets.
00:57Register_shutdown_function requires one parameter, a string containing the name
01:03of the function to call.
01:04I'm going to name this function shutdown_notify.
01:08Next, I'm going to define the function itself, start with a comment.
01:13Send a notification on a shutdown caused by an error.
01:19Next, the function itself which takes no parameters.
01:24Using the function error_get_last, get the last error if any, so $error = error_get_last();.
01:32If the error is not empty, it returns an associative array.
01:36One of the keys, type, contains the integer value of the error level,
01:41which I can use to determine the kind of error;
01:44if (!empty($error) && in_array($error['type'], array(E_ERROR, E_ERROR, ))).
02:00So if it's not empty and it's one of those types,
02:05then show a graceful message to the user.
02:06It can have markup, but don't assume any particular styles are available.
02:10So echo'<h1>Sorry, something went horribly wrong;
02:16the team has been notified.</h1>';.
02:22For now, let's just show the contents of error and the server
02:26superglobal, var_dump($error);
02:30and then var_dump($_SERVER);
02:33save and return to the browser and refresh.
02:37The three Triggered error levels are shown and the last trigger error
02:40was fatal and caused the shutdown.
02:42Looking at the contents of the error, I can see four keys and I can also see
02:46the contents of the server superglobal.
02:50This is very helpful information for a programmer or administrator,
02:53but shouldn't be shown to a user.
02:55If the server has e-mail setup, I can take this opportunity to send
02:59a notification describing the problem.
03:01Do be careful when doing this, as if there is a major problem on a high-traffic site,
03:05thousands of e-mails could be sent.
03:07So consider writing a mechanism that can throttle the volume of messages
03:10by checking a local file.
03:11Returning to the IDE, I'm going to remove the var_dump and instead compose an e-mail,
03:16start with a to, $to = 'user@example.com';.
03:22Next, the subject which should provide some high-level context,
03:37$subject = "[{$_SERVER['SERVER_NAME]}] fatal error in {$error['file']} on line {$error['line']}]";
03:48Then I'll create a message.
03:49I'll use var_export($error) then set the second parameter to True to return the output.
03:57I'll concatenate the end of line character at the end to be clean.
04:01I'll add to the message and also export server superglobal.
04:07Now that the message has been created, simply use PHP's mail function.
04:12I'm going to leave mail commented out for now, so I don't send a large number of e-mails
04:16to myself while demonstrating.
04:17Mail $to, $subject and then the $message and remember to comment it out and save.
04:26Return to the browser and Reload.
04:28The shutdown function is now triggered, but instead of showing debugging
04:32information to the user, it's prepared it for e-mail.
04:35Before I move on to third-party tools, there is one more PHP debugging tool
04:39that I would like to demonstrate, debug_backtrace.
Collapse this transcript
Deciphering backtraces
00:00The PHP function, debug_backtrace generates a backtrace.
00:04A backtrace is a report containing a sequence of nested function and method
00:08calls to the point where the trace is generated, which gives context
00:12to a developer to know what happened.
00:14This is especially useful when working with frameworks and nested structures
00:17with many file inclusions, functions calling functions, and so forth.
00:21Debug backtrace returns associative arrays keyed by the frame, which is like
00:25a depth in ascending order where the lowest number is the last thing to be executed.
00:30For each frame, you will be given the function name, line and file if any,
00:35class and object if any, and arguments.
00:37I'm going to demonstrate how to generate and read backtraces in order to get context.
00:42Backtraces are excellent for development but not for reporting errors,
00:46and I'll show you why that is in a moment.
00:48First, go to the IDE and navigate to after the shutdown function.
00:53I'm going to create a short sequence of functions that call one another,
00:56and at the end I'll display a backtrace.
00:58I'll keep it simple, function A takes an argument and passes a different argument
01:03to function B. Function B also takes an argument and calls function D.
01:11Function D displays the content of debug_backtrace on the screen.
01:17So var_dump(debug_backtrace(});.
01:20Finally, call the first function, save then go to the browser and refresh.
01:26The backtrace is rendered with frame zero containing the last called function, function D.
01:30Scrolling through I can see that each of the functions in order that
01:35they were called, and on the very last step, the required ones that was used
01:39to include the demo file into the exercise index.
01:42It would be logical to want to include a debug_backtrace in the fatal error behavior,
01:46but it won't work in the way one might expect.
01:49Go back to the IDE and move the three intentionally triggered errors
01:54after the debug_backtrace in function D.
01:58Then, add the same var_dump debug_backtrace to notify shutdown after the echoed message
02:03to the user, save then return to the browser and refresh.
02:09Scrolling to the bottom past the first debug backtrace, I could see a second
02:12debug_backtrace, but it's strangely empty.
02:15This is due to other PHP shutdown functions I mentioned previously.
02:19Backtraces are useful but are they limited to nonfatal errors? Not necessarily.
02:24In the next chapter, I'll introduce Xdebug, a PHP extension that provides
02:29additional functionality for developments that will, among other things,
02:33produce backtraces on fatal errors.
02:35Throughout this chapter, I've laid the groundwork essential to understanding
02:39how PHP handles errors and warnings, starting with PHP error levels and consequences.
02:44Next, I demonstrated how to configure PHP to report what went wrong
02:49and how to log those errors to file.
02:50I intentionally triggered and repaired a number of common kinds of PHP errors
02:54then demonstrated a technique for gracefully handling fatal errors.
02:58Finally, I showed how backtraces can be deciphered to give context into the call stack.
03:03PHP has an additional mechanism for handling object-oriented errors known as exceptions.
03:08Exceptions can be triggered, known as throwing, and handled
03:12using a technique known as catching.
03:14I've covered this topic in-depth in a different course, Object-oriented
03:17Programming with PHP here in the lynda.com Online Training Library.
03:21The two segments in particular are Error Handling with Exceptions
03:25and Customizing PHP Exceptions.
Collapse this transcript
2. Introducing Xdebug
What is Xdebug and how can it be used?
00:00While the core PHP distribution contains many tools for debugging and error handling,
00:04it does have some inherence limitations such as the inability
00:08to perform stack traces on fatal errors.
00:10To provide this missing functionality and to give additional introspection
00:14into script operation, the PHP extension Xdebug was created in 2002.
00:19Currently at version 2.2.1, Xdebug is a debugging and profiling tool
00:25for use on development servers.
00:27By providing stack and function execution traces and error messages,
00:31Xdebug allows fatal errors to be debugged with the same clarity available during
00:35normal program execution without the limitation of the shutdown processes.
00:40Xdebug is also a profiling tool, which tracks how long it takes to execute
00:44each part of a script, which is useful for finding performance bottlenecks.
00:48Additionally, Xdebug provides mechanism for remote debugging allowing script execution
00:52to be paused at any arbitrary point and the contents of variables
00:57to be read and manipulated.
00:59Xdebug is a powerful debugging tool and as such should not be used
01:03on anything other than private development servers.
01:06There are a number of reasons for this.
01:08First, it's a security risk in that it exposes very low-level information about the execution
01:11of a script, which provides malicious users with privileged information.
01:17Additionally, the remote debugging functionality does not require any authentication
01:20or encryption out of the box leaving the proverbial door wide open.
01:25Finally, Xdebug can record and collect a large amount of information, which in turn
01:29consumes more memory and processor resources than a regular script execution.
01:34For a developer, this difference is negligible but for a public facing server,
01:38the aggregate performance hit will be significant enough to cause problems.
01:42With this context, let's move on to installing the Xdebug extension.
Collapse this transcript
Installing the Xdebug extension
00:00If you are using a Web server stack such as MAMP or WampServer then Xdebug
00:05may be already available, so please refer to the appropriate documentation in those cases.
00:11These instructions assume that PECL is already installed on the target server,
00:15which is part of the PEAR packaging system.
00:17If not, please install PEAR.
00:20Additionally, administrative credentials are required.
00:24The following technique was adapted from documentation from Xdebug.org.
00:29Please refer to the documentation for additional troubleshooting
00:32and installation alternatives.
00:34Netbeans.org also provides some comprehensive installation instructions
00:38with details not found on the Xdebug site.
00:41By using PECL, Xdebug installation is straightforward but does have some little
00:46pain points that I will help mitigate.
00:49From the terminal, connect to the development server.
00:54Next, use PECL to install Xdebug.
00:57This performs the majority of the dirty work, including the downloading and compiling,
01:00but does not actually configure Apache, so type the following command:
01:04sudo pecl install xdebug, it will ask me for my password.
01:12When completed, take note of the line, Installing, followed by a path.
01:16This is where the extension was placed.
01:18I can see that the file was placed in /usr/lib/php5/20090626/xdebug.so.
01:27Depending on your system configuration, this may be slightly different.
01:31There is a second line, which should be ignored.
01:33You should add extension=Xdebug.so to php.ini.
01:38Again, ignore this as this message is created by PECL but not Xdebug.
01:42This is a known minor issue.
01:44Now that Xdebug is installed, I need to configure PHP to load the extension.
01:49Xdebug has a number of configuration options, but for the time being
01:53I'm just going to load the extension.
01:54Edit the PHP configuration, sudo nano-w /etc/php5/apache2/php.ini.
01:57I'm going to press Esc+/ to navigate to the end of the file.
02:08Add the following line, zend_extension= followed by the path shown
02:16in the PECL Xdebug installation. So in my case, its
02:17"/usr/lib/php5/20090626/xdebug.so" Exit, Ctrl+X and Y to save.
02:32Restart the Web server to allow the PHP configuration changes to take effect,
02:36sudo/etc/init.d/apache2 restart.
02:40
02:43Now that the Web server has restarted, switch to the browser and reload the page.
02:47If Xdebug was installed correctly, each of the triggered error levels
02:53will also have a stack trace after it.
02:55If the stack traces are not seen, check the previously mentioned installation
02:59documentation for additional pointers.
03:02Xdebug has been installed, but where to go from here?
03:05First, I'm going to discuss how Xdebug handles variable display.
Collapse this transcript
Displaying variables with Xdebug
00:00Xdebug replaces var_dump and var_export with its own version, which includes
00:05different colors and highlighting for different types, limits on array elements
00:09and object properties, a maximum depth to prevent infinite recursion problems,
00:14and string length to reduce the chance of overloading the browser.
00:18It's on by default, but is only enabled if PHP is configured to render error messages
00:23as HTML, and HTML errors are off by default.
00:28Return to the terminal then edit the PHP configuration again,
00:33sudo nano -w /etc/php5/apache2/php.ini.
00:33Press Ctrl+W to search for HTML_ERRORS =.
00:48Change its value to On.
00:49Then exit and save, then restart the Web server.
00:56Return to the browser and refresh.
01:01Immediately, the difference will be noticeable and the notice, warning and fatal errors
01:05are now displayed in a table.
01:08The trigger error functions are clickable, linking to the documentation on php.net.
01:13Xdebug also adds an improved version of PHP's debug_zval_dump called
01:20xdebug_debug_zval, which includes structured information about variables,
01:25including type, value and reference count.
01:29By using the variable name to look up the variable, as opposed to passing
01:32the variable itself, Xdebug's implementation is more accurate.
01:37This is useful when I'm not sure if the variable is passed by reference,
01:40especially when there is a long chain containing the same variable.
01:44In PHP, references allow variable content to be accessed by different names.
01:49Unlike Z pointers, they're not actually memory addresses;
01:52instead references are symbol table aliases.
01:55Xdebug_debug_zval displays the reference count, which is the number of aliases
02:01a particular variable has in the symbol table.
02:03For example, when a variable is passed between functions, the count will increase.
02:09The is_ref flag indicates the variable was passed by reference.
02:13So if I were to alter the content of that variable, the original variable
02:17would also be affected.
02:19To demonstrate this, I'm going back to the IDE.
02:22In function A, use xdebug_debug_zval to display information about arg,
02:30then pass arg to function B. In function B, add an ampersand in front of arg to pass by reference.
02:40Next, I'm going to use xdebug_debug_zval, on arg again before the call to function D.
02:47Then, in function D, add the same line to debug the variable without a reference.
02:55Save, then return to the browser and refresh.
03:01The first pram indicates two reference counts.
03:04The second shows three reference counts and that the variable was passed by reference.
03:09Remember, the reference count is the count of aliases a particular variable
03:14has in the symbol table.
03:15Another useful function allows the display of all the declared variables
03:19that are in the current scope.
03:20However, it requires a little extra configuration to collect variables during execution.
03:25Go to the terminal and edit php.ini again, press Esc+/ to navigate
03:32to the end of the file and add the following, xdebug.collect_vars = 1.
03:41While I'm at it, I will also configure the stack traces to show the contents
03:44of local variables as well.
03:46xdebug.show_local_vars = 1.
03:53Finally, I will also tell Xdebug to keep track of the names and the contents
03:57of parameters for each step of the call stack.
04:00This will cause a performance hit in very large scripts but for now its fine.
04:04xdebug.collect_params = 4, exit and save and restart the Web server.
04:15Return to the IDE and remove the calls to xdebug_debug_zval and the debug backtraces
04:25in function D and notify shutdown.
04:32In function D, add the following to the top, declared = variable, then var_dump
04:40(xdebug get_declared_vars).
04:47Save, then, go back to the browser and refresh.
04:52The names of each of the variables in the current scope are shown as strings
04:55in an array both arg and declared.
04:57And the contents are not shown by Xdebug yet declared vars.
05:02Additionally, information about the individual arguments and function calls
05:06are shown in the stack traces.
05:07While it can be useful to get this information by explicitly adding
05:11xdebug_get_declared_vars, and other statements, is this the only way?
05:16What about pausing script execution?
05:19Xdebug remote debugging supports this functionality.
Collapse this transcript
Exploring remote debugging principles
00:00One of Xdebug's features is the ability to remotely debug script execution.
00:05In particular, it provides introspection into data structures and allows code execution
00:09to be stepped through for debugging.
00:12Remote debugging is turned off by default, so I will demonstrate how to turn it on.
00:17Remember, this is for development purposes.
00:20Do not use remote debugging in production environments.
00:23Before I jump into configuration and demonstration, I will describe the workflow
00:27where Xdebug interacts with the remote debugger.
00:30This will provide context and highlight areas that should be investigated
00:34if there are connection problems.
00:36First, the Web server listens for request, same as usual.
00:39However, when a request is made, Xdebug will attempt to connect to the debugger
00:44on the remote IP from the headers via port 9000.
00:48If found the IDE will give instructions on where to pause and when to continue.
00:53Whenever Xdebug is paused, it will provide information back to the debugger,
00:57so this is a two-way communication.
01:00Once the debugger says Xdebug can continue, the http response is updated.
01:06This loop will continue until script execution is complete
01:09or Xdebug is commanded to halt.
01:11Now that I described the workflow and the moving parts, the configuration and use
01:15should make a bit more sense.
01:17Switching to the terminal, edit php.ini again. Navigate to the end of the file.
01:26First, I will enable remote debugging xdebug remote_enable = 1.
01:35Then, I will tell Xdebug to attempt to connect to a debugger
01:39if a remote debugging session has started via the IP from the headers.
01:42There are strategies for multiple users on one server but they're
01:46out of scope for this course.
01:47I'm focusing on one developer for now.
01:48Xdebug.remote_connect back = 1, exit and save.
01:57Restart the server.
02:00Now that the server is restarted, I need to make sure that two-way communication
02:04is possible on the remote debug port 9000.
02:06In my case, since I'm using a virtualized development server, I'm confident
02:11that there is no port conflict so I do not need to forward any ports.
02:15If there was a remote server, then I would need to make sure that the firewall,
02:19if any, was configured correctly to allow the bidirectional traffic.
02:23Additionally, if there was a router between you and the remote server,
02:27the port would have to be forwarded.
02:29Before I demonstrate how to use remote debugging, I'll quickly describe
02:32the practical workflow within the IDE.
02:35This applies to NetBeans specifically but is very similar to other IDEs as well.
02:40First, I'll set the breakpoints, which are the places within script execution
02:44where Xdebug will pause and send debugging information.
02:47Breakpoints set within the IDE are on a specific line of code and are simply
02:52on or off with no logic.
02:54If I want to use logic, I can add a call to function Xdebug break,
02:58which will have the same effect.
03:00Next, I'll tell the IDE to debug the project.
03:03This will launch the browser, opening the project URL
03:07with an additional parameter in the URL that sets a cookie and tells Xdebug
03:11that this is a debugging session.
03:13This script will execute until Xdebug reaches the breakpoint, at which point
03:17Xdebug will send debugging information back to the IDE.
03:21From the IDE, I will tell Xdebug to go on to the next step, which is either
03:25another breakpoint or the end of the script.
03:27With that context, I'll demonstrate how to remotely debug with NetBeans and Xdebug.
Collapse this transcript
Xdebug remote debugging with NetBeans
00:00NetBeans 7.2 has Xdebug support built into it, but some minor configuration is needed first.
00:06I'll need to change the default debugging configuration.
00:09Edit the NetBeans preferences, click PHP, then Debugging.
00:16Uncheck Stop at the First Line, which would pause execution when it reaches
00:20the first line, which I find distracting and I always skip. Click OK.
00:25Right-click the Project and go to Properties.
00:30Under Run Configuration, specify the PROJECT URL.
00:34In my case it's sandbox.dev.8080, click OK.
00:42NetBeans is now configured to work as a debugger.
00:45I've described the NetBeans debugging workflow, now I'll demonstrate it.
00:49For NetBeans, I'm going to set a breakpoint.
00:51Scroll down to the var dump xdebug_get_declared vars and left-click
00:57just to the left of the line number.
00:59The entire line will be highlighted in red.
01:02This means a breakpoint will be set, so Xdebug will pause execution on that line.
01:07At the top of the toolbar, click the green arrow with a breakpoint icon
01:11to start the debugger and launch the browser.
01:16Notice that the browser is still loading.
01:18The session isn't complete yet as Xdebug is paused at the breakpoint.
01:22Go back to NetBeans, at the bottom, debugging information is now shown.
01:27The first new tab is variables, which will show all the superglobals like Cookie and Server
01:32in addition to all the variables within the current scope of the breakpoint.
01:38The contents of each variable are shown.
01:41I can see that declared is set to the string variable.
01:44I also have the ability to edit the variables, which is very useful.
01:48Double-click the variable value to edit in place or click in the three dots
01:53to the right to edit in a pop up.
01:55Change the value to "edited" and click OK.
02:01The next tab, Call Stack, shows the call stack ordered by the most recent at the top.
02:06I can see that index required the demo file, then the function 'a' was called,
02:10followed by 'b' then 'd.' I can double-click on any of these items
02:15to jump to that place and code.
02:18The line of code is highlighted as the Call Stack line, and if I hover over
02:22the symbol on the left, the hovered text will say Call Stack Line.
02:26The final debugging tab, Breakpoints, is a list of all the breakpoints
02:31that have been set and they can be toggled or disabled in this interface.
02:35Now that I've edited the value of the variable, I'm going to continue the execution.
02:40Up on the top, there is a green Play button; click it to Continue.
02:45There is also a keyboard shortcut, F5, which will do the same thing.
02:49The script execution will complete and switching back to the browser,
02:53I can see that the entire page is loaded.
02:55The contents of the edited variable are shown.
02:58When I reload the page, the debugging process is started again
03:03and the execution is paused.
03:05If I navigate to just index.php, the cookie set still tells Xdebug to pause.
03:12To stop the debugging session, return to NetBeans and click the big red stop button.
03:19This will open a new browser window with a different parameter, and a short message;
03:22DEBUG SESSION ENDED will be shown.
03:25In the next segment, I'll show how Xdebug can be used to profile performance
03:29to find bottlenecks and scripts.
Collapse this transcript
Profiling performance to find bottlenecks
00:00One of Xdebug's features is a profiler, which analyzes program execution
00:04to measure memory usage, duration, and frequency of function calls.
00:09The profiler generates files that can be analyzed with third-party tools,
00:14known as cache grind, which I will demonstrate.
00:16Similar to other techniques that I've demonstrated, profiling is susceptible
00:20to the observer effect, as it has an impact on the speed of program execution.
00:25Therefore, Xdebug leaves profiling off by default.
00:28I'm going to selectively enable the profiler.
00:31Meaning, instead of recording every single page, I will need to pass an additional header
00:35or cookie to manually trigger it.
00:38This way I don't have to sort through all the generated files
00:41to find the particular one I'm interested in.
00:43By default, Xdebug will write to/tmp/cashgrind.out.processID.
00:51Switching to the terminal, I'm going to edit the PHP configuration.
00:55sudo nano -w /etc/php5/apache2/php.ini.
01:04Skipping to the end of the file, I'm going to add one line that will allow
01:08Xdebug's profiler to be manually triggered.
01:10xdebug.profiler_enable_trigger = 1.
01:19Exit and Save then Restart the Web server, sudo /etc/init.d/apache2 restart.
01:28Now that Xdebug is ready to profile, I'm going to demonstrate how profiling
01:31can find a bottleneck.
01:32Go to the IDE and scroll to the bottom and add two new functions.
01:38The first will be to simulate slow execution of a function by counting to 100,000.
01:44Slow execution, so function slow for ($i = 0; $i < 100000; $i++)
01:58and then do nothing.
02:00The second will be to simulate an even slower function,
02:05slow execution even more.
02:09Function slower, usleep(50000).
02:17Before the function call to a, add calls to the new functions.
02:21I'm going to loop the execution of slow in order to simulate a real-world problem,
02:25where slow function gets called a lot of times.
02:28for ($i=0; $i<50; $i++) and we'll call slow and then we'll call slower once.
02:41Returning to the browser, I'm going to manually profile page execution.
02:46Navigate to the root, but this time we'll specify index.php and pass
02:51the parameter XDEBUG_PROFILE = 1 in the URL.
02:57Script execution takes quite a bit longer now and the stack trace
03:01from the errors highlights that.
03:03Now to analyze the profile. Included in the Exercise Files, is a copy of webgrind,
03:07a utility for analyzing profiler results.
03:11Navigate to sandbox.dev port 8080/webgrind.
03:18At the top select Show 100% of the Auto (newest) in microseconds and click update.
03:25A large table is shown.
03:28On the left is an icon showing the type of call.
03:31Hovering over the image will indicate what kind it is.
03:35In this case, there are three procedural calls and the rest are internal.
03:40The next column is the Function column, which displays the function in question.
03:44If I click the arrow next to the Function, it will show where the function was called from
03:48and provide additional context.
03:52To the right is a link to a formatted view of the code in question.
03:56As this is a development site not visible to the public, it's not a security risk,
04:00but do consider the audience before using this utility.
04:03Invocation Count is the number of times the Function has been evoked.
04:08Total Self Cost is the aggregate cost in milliseconds for the function itself,
04:13while Total Inclusive Cost is how long the function takes to execute,
04:17including everything that was executed in it.
04:19There is an additional button, Show Call Graph, which uses Python
04:23in an additional library for generating a nice flowchart of script execution.
04:26However, this requires a lot more configuration and installation,
04:30so I will not cover it within this course.
04:32By default, webgrind sorts by Total Self Cost.
04:35This indicates that the function slow is the slowest.
04:39However, this isn't useful because comparing to the PHP function usleep,
04:43it's saying that it has a high self cost as well and the slower function,
04:48which we know is slow, has a Total Self Cost of 48.
04:52Since I know that the PHP core function is not the problem, I'm going to click
04:56Hide PHP functions and press update.
04:59Now, only the functions I wrote are shown.
05:02Notice that the Total Self Cost of slow is the highest along with the Invocation Count.
05:07Compare this to slower, which has a very tiny Self Cost.
05:10What this indicates is that something in slow is causing a problem, as the Self Cost
05:14is identical to the Inclusive Cost.
05:17In contrast, the Self Cost for slower is basically nothing,
05:21while the Inclusive Cost is very high.
05:23This indicates that something that slower is calling is actually slowing down execution,
05:27not the slower function itself.
05:31By comparing Invocation Counts and Costs, I can make a determination
05:35about the greatest return of investment of my optimizing efforts.
05:39In particular, if something is invoked a lot and has nearly identical Self and Inclusive Cost,
05:43it's a good candidate for optimization.
05:47Fortunately, in this case, I know that slow and slower are the problem
05:51and serve no functional purpose, so the optimization step will be their elimination.
05:56In this chapter, I've introduced Xdebug, the debugging PHP extension.
06:01I first discussed what Xdebug is and how it can be used.
06:05Then I demonstrated how to install Xdebug and gave links to additional documentation
06:09if there were problems.
06:11I showed how Xdebug changes how Variables are displayed and how Call Stacks can be shown.
06:17Next, I discussed the Xdebug remote workflow, configuring and performing
06:21remote debugging with NetBeans.
06:23Finally, I demonstrated how Xdebug can be used to profile script execution
06:28and how to analyze the results.
06:30In the next chapter, I'll turn to Web browsers and how they can be leveraged
06:34as effective debugging tools.
Collapse this transcript
3. Debugging from the Browser
Extending Firefox with Firebug and FirePHP
00:00When debugging interactions, and especially Ajax interactions,
00:04the importance of the use of the browser as a debugging tool becomes clear.
00:07Most modern browsers come equipped with development tools that provide
00:11HTML and Style Inspection, JavaScript profiling and debugging and a JavaScript console.
00:16Mozilla Firefox lacks many of these tools out-of-the-box.
00:20Instead, a comprehensive suite of Web development tools can be found in Firebug,
00:24a free and open-source extension that includes among other things,
00:28HTML and Style Inspection and a JavaScript debugger, originally created in 2006
00:33by one of the Firefox creators.
00:35Firebug provides a wide array of functionality that many Web developers
00:38have become accustomed to in implementations of the WebKit DevTools
00:42and Chrome Developer Tools.
00:44All these tools are client-side only.
00:46Meaning, they are fantastic for debugging things locally,
00:49but have no introspection into what's happening on the server.
00:52This means PHP, which runs exclusively on the server, doesn't normally
00:56have a mechanism for communicating with the local browser debugger.
00:59Throughout this chapter, I will demonstrate several debugging consoles
01:03that interact with PHP and a standalone debugging library that renders as an overlay.
01:08To save time, I have installed and arranged each server-side tool in the Exercise Files
01:12and have provided a centralized array in index.php that can be used
01:17to selectively enable and disable them.
01:20I will describe each tool's integration, so it can be use as a template
01:24for other installations.
01:25I'll start with Firebug and FirePHP.
01:28FirePHP allows logging to the Firebug console with a PHP function call,
01:33including status messages and object contents.
01:36There are several distinct components for the solution stack:
01:38Firefox, the Web browser; Firebug, the extension that provides the debugging functionality;
01:43FirePHP, the extension that interacts with the server;
01:47and FirePHPCore, the PHP library that sends information via headers
01:52to the extension from the server.
01:54I'll start by opening my Firefox browser.
01:57First, I'll install Firebug.
01:59Navigate to getfirebug.com, then click Install Firebug.
02:05I want Firebug 1.10.6 for Firefox 16.
02:10Click download, then add to Firefox. Click Install Now.
02:15Click OK and close the new tab.
02:20Next, I'll install the FirePHP extension.
02:23Navigate to firephp.org.
02:28Select FirePHP Extension for Firefox 8 and above, and Firebug 1.9 and above.
02:33Click the link for the Stable Channel, click Continue to Download
02:39and Accept the new BSD License and Install.
02:44Now, that both the extensions are installed, restart Firefox.
02:47Navigate to the Exercise Files, http//sandbox.dev.8080.
02:55Click the Firebug icon in the upper right corner.
02:59You can safely close this one-time tab.
03:03Verify that both Console is enabled and Net is enabled.
03:10The FirePHP Library Installation is straightforward, just download and include a PHP library
03:14at the top of the script.
03:17To save time, I've installed the FirePHP Library in the provided Exercise Files.
03:22Switching to the IDE, open index.php and navigate the tools array at the top of the script.
03:29Set the key firephp to True.
03:32Scrolling down, all this does is require the firephp library and start output buffering
03:37as FirePHPCore sends information in the headers and I want to make sure
03:41that no content gets sent before FirePHPCore gets a chance to work.
03:46The FirePHPCore library itself has been organized into the subfolder, FirePHPCore.
03:51Now, that FirePHP has been enabled and configured, it's time to use it.
03:56Remember to save index.php before continuing.
03:59I'm going to make a quick backup of demo.php as I'm going to demonstrate
04:03different techniques later and I want the same starting point.
04:05So, I'm going to right-click on demo and go to Copy and then Paste back in the Source Files.
04:12I'm going to scroll to the top of the script, and I'll start by getting
04:16a FirePHP instance using the built in singleton pattern after the ini_sets.
04:20Meaning that there will only be one FirePHP instance as long as I use
04:24the static function to get it.
04:25Get FirePHP instance and then $firephp = FirePHP::get Instance (TRUE).
04:39As FirePHP is a logger, let's log some actions.
04:42Following the shutdown function registration, I'm going to add log entry,
04:45$firephp, I'll call the function log, which takes a string
04:52Registered notify_shutdown.
04:56This will show up in the Firebug console log.
04:59Log can also take a second argument, a label to be shown in the interface.
05:02Scroll down to the for loop and create a log entry, this time with the label performance,
05:07$firephp->log('Executing slow loop.') and we'll add up the label Performance.
05:17Save and then return to Firefox, click on Console and reload the page.
05:22The Console shows two log entries.
05:25The second log entry is pre-appended with the label.
05:28Hovering over each of the log entries, I can see exactly what line in what file
05:32the log entry was triggered in.
05:34Firebug also supports three basic error levels as well.
05:38Switching back to the IDE, navigate to function d where the errors are triggered.
05:43As I'm currently in the function scope, get the singleton instance of FirePHP,
05:48$firephp = FirePHP::getInstance(TRUE)
05:55Before the notice, I'm going to use a different function, as this system
05:59is compatible with Firebug, a JavaScript tool,
06:01there is no PHP notice support.
06:03Instead, I'll use info, $firephp->info('Triggered notice.')
06:10Do the same for warning, $firephp, which is just named warn, 'Triggered warning.'
06:18Finally, error which is just error, $firephp->error('Triggered error').
06:27Return to the browser and refresh.
06:30Notice that when I trigger an error, an error count is showed in the Firebug icon,
06:34which is an excellent visual indicator to developers.
06:37The triggered notice, warning and error are shown with different icons
06:42and each of them can be hovered over to tell you the file and line number.
06:45Switching back to the IDE, I'm going to show you two more tools available in Firebug.
06:50Instead of var_dump, I can also log any PHP structure.
06:54Replace the var_dump Xdebug_get_declared vars with the firephp log.
07:01$firephp, log, xdebug, get declared vars, and then we'll followup
07:06with an optional label, xdebug_get_declared_vars.
07:11We can remove the var_dump.
07:13Additionally, FirePHP supports stack traces using the trace function, $firephp trace,
07:17which we'll give a label just as Trace.
07:23Save, then return to the browser and refresh.
07:25I can see the Xdebug_get_declared_vars in the Console, but I can't see all of it.
07:30Click on the array, and a Variable Viewer will pop up.
07:33This is much more useful. Click Close.
07:37The Trace is displayed as well, click on it to see the Stack Trace, which is fairly simplified.
07:42The combination of FirePHP and Firebug is a powerful one,
07:46but requires a particular browser, however, there are other alternatives.
Collapse this transcript
Integrating ChromePHP
00:00ChromePHP is a console logging extension for Google Chrome. It's similar to FirePHP,
00:05but the feature set is a little bit more limited, including a lack of stack traces.
00:09While it's not as robust, I can demonstrate how to make it useful.
00:13To use ChromePHP, there are a number of components that need to be assembled.
00:17First, the Chrome Browser, which hosts the ChromePHP extension. Finally
00:22the ChromePHP class, which resides on the server and sends the headers to be logged.
00:27Switching to the Chrome Browser, navigate to http://www.chromephp.com.
00:33Click the Download link on Step 1 to install the extension.
00:37Click ADD TO CHROME and Add.
00:40You can close the window now for the Chrome Web Store, then switch
00:44to the IDE and open index.php.
00:47I have already installed the ChromePHP Library to save time,
00:51but configuration is required.
00:52Make sure FirePHP is set to False and then set ChromePHP to True. Save index.php.
01:01The only installation step is shown below, which is to include the library,
01:05and start output buffering.
01:07Copy the contents of the backup demo into the demo file
01:13to remove all the incompatible PHP debugging information.
01:16Scrolling to the top, instead of the singleton instance,
01:20ChromePHP just uses static methods.
01:21I'll start by logging the registration of the shutdown function.
01:25Just after the registration, add the following.
01:27ChromePhp::log('Registering shutdown function.') Save, then, return to Chrome.
01:38Navigate to the exercise files, then, click the ChromePHP icon in the upper-right.
01:46Reload the page and open up the Console.
01:51The message Registering shutdown function is shown, but no context is shown
01:55even if I hover over the message.
01:57Right-click on the ChromePHP icon and go to Options.
02:01I'm going to check Show line numbers and click Save Changes.
02:07Going back to the debugging PHP window, I'll reload the page.
02:11This time the file and line number are shown, but on a separate line.
02:15While this is helpful, this is a wasteful way of doing it.
02:18Go back to the ChromePHP Extension Settings and disable show line numbers then Save Changes.
02:23Returning to the IDE, I'm going to make the message a bit more verbose.
02:27Unlike FirePHP, ChromePHP log functions take the label first, then the message.
02:33Using magic constants, I'll provide context.
02:35So, go to log(_File_ . concatenate, the semicolon, then the line and comma,
02:46and then the Registering shutdown function.
02:48Save, then return to Chrome and Reload.
02:52This time the file and line are shown in line.
02:55This method is more compact, but requires additional code.
02:58Returning to the IDE, go to function d.
03:03Replace the var_dump of xdebug_get_declared_vars with a log.
03:06Remember, label first, then content.
03:09So ChromePhp, log, then we'll give the label xdebug_get_declared_vars
03:18and then the function call itself.
03:20A neat feature of ChromePHP is the ability to group a series of log messages.
03:24I'm going to log a backtrace and I'll start by defining the name of the group
03:28using Group Collapsed.
03:30ChromePhp, groupCollapsed and we'll give it a name, backtrace.
03:35Next, log the debug a backtrace array. So ChromePhp, log, debug_backtrace.
03:44Finally, ends the group.
03:46ChromePhp, groupEnd.
03:51ChromePHP supports three error levels as well.
03:54Before the notice and the same as FirePHP, use info.
03:58ChromePhp, info, Triggered notice.
04:04Then before the warning, a warn, ChromePhp, warn, Triggered warning,
04:12and finally, an error.
04:13ChromePhp, error, Triggered error, Save, then, return to Chrome and Reload.
04:21The contents of xdebug_declared_vars are shown in line, but can't be clicked on.
04:27Backtrace can be opened and debugged like a JavaScript object.
04:34The Triggered notice shows the same as a log message.
04:37The warning and error are displayed with appropriate icons, but the Triggered error
04:41is treated the same as a JavaScript error.
04:43ChromePHP is a good tool, but does suffer from some limitations.
04:47What if I wanted to debug without any extensions with any browser?
Collapse this transcript
Using PHP_Debug to debug without a console
00:00PHP Debug is an open source, debugging library written in PHP that resides
00:05completely server-side, so no particular browser or extensions are needed.
00:09Some of its features include:
00:10displaying the server configuration and global variables by default;
00:14stack traces to provide context about program execution; variable dumping;
00:19multiple rendering options, so either the results can be floated as a DIV or appended
00:23as an HTML table at the end of execution; and PHP error-handling replacement.
00:28So instead of Xdebug other debugging tools, PHP_Debug can provide contexts upon errors.
00:34PHP_Debug can be installed via PEAR.
00:37Additionally, PHP_Debug uses another library.
00:39Text-Highlighter also available via PEAR.
00:42However, PHP_Debug has some hard coded configuration within the library itself,
00:45and as of this writing, Text_Highlighter is no longer maintained
00:50and contains a strict error that I fixed.
00:52Therefore, I just included the libraries in the PHP_Debug directory
00:56to simplify installation.
00:57Opening the IDE, I'm going to restore demo.php to the state it was
01:02at the beginning of the chapter.
01:04So I'm going to just copy and paste.
01:07Next, open index.php, and disable ChromePHP and enable PHP_Debug by setting it to True.
01:17Integration happens in several places.
01:19The setup includes creating an array describing relative paths
01:23and some other configuration information. Then the included path is updated
01:27to include the path to the libraries.
01:29And finally, the require_once to the PHP folder.
01:32Unfortunately, PHP_Debug does not use a singleton pattern, so as a workaround,
01:36I created a globalvariable, which I do not consider to be a best practice.
01:40Scrolling down, JavaScript and CSS is also added and at the bottom of the script
01:46the output of PHP_Debug is rendered.
01:49This is a lot more intrusive than the other debugging libraries,
01:52but there is a method to the madness.
01:53Navigate to PHP_Debug> Text>Highlighter.php.
01:58On line 199, I changed the function factory to static to avoid a strict error.
02:04There is one configuration change that is needed.
02:07Navigate to PHP_Debug_ShowSource.php.
02:11On line 24, there is a hard coded ALLOWED_PATH.
02:15I understand the need of having security, but having to edit the library
02:18to reconfigure it is suboptimal.
02:20Change the ALLOWED_PATH to reflect your exercise Web root.
02:24Make sure you save and go to the browser and reload the page.
02:29Notice that the page looks a lot cleaner.
02:31All of the Xdebug error handling has been hidden along with the shotdown function.
02:35There is also a new toolbar in the upper-right.
02:38Click vars & config.
02:39This will show a number of superglobals, PHP Configuration and every file that
02:44was loaded with a link to view the source.
02:47Going back, click on logs & messages.
02:51The Triggered PHP errors are shown with context.
02:55Clicking on the watch, execution time is displayed, broken into PHP and SQL.
03:01While the implementation is decidedly imperfect, PHP_Debug is a powerful tool.
03:06Opening the IDE, I will demonstrate how to perform
03:10some of PHP_Debugs logging facilities.
03:11Open the demo file.
03:13After the ini_sets, get the global PHP_Debug object, global $PHP_Debug.
03:22Then, use the add function for the shutdown function,
03:26which will add the message to the log.
03:27$PHP_Debug->add('Registering shutdown function.').
03:33Next, I'll demonstrate how to dump a variable by replacing the var_dump in function d.
03:36As the scope is within the function, I'll need to get the global objects again,
03:41global $PHP_Debug.
03:44Then, PHP_Debug uses the function dump as a replacement to var_dump.
03:49So $PHP_Debug->dump.
03:53Finally, I will demonstrate how to add benchmarks to log execution time.
03:57Scroll down to after the function d declaration.
04:00I'm going to benchmark the slow function loop.
04:03The add function returns an objects that offers additional controls,
04:07such as setting to start and end times.
04:09So we'll say $debug_line=$PHP_Debug->
04:15add('Slow loop benchmark') and then
04:21$debug_line->setStartTime.
04:22Then after the loop, $debug_line->setEndTime.
04:28Save, then return to the browser and refresh.
04:34For the first time since I started, the page contains no errors or debugging information
04:38and only displays the message that we set after the fatal error.
04:42Clicking on logs & messages, I can see the slow loop benchmark has a time next to it,
04:47788 milliseconds, and the variable dump is displayed as well.
04:52If I click on the watch, the slow loop benchmark has been broken out separately.
04:56Throughout this chapter I've explored how to debug from the browser.
05:00In particular, I've demonstrated how to extend Firefox with Firebug and FirePHP;
05:05How Chrome PHP can provide similar logging functionality to Chrome;
05:09and finally, how to eliminate the need for browser extensions by using PHP_Debug
05:13to debug without a console.
05:15Now that I've discussed a wide variety of server side and client side tools,
05:19I'll describe a number of tried and true debugging best practices
05:22and offer ideas of where to go from here.
Collapse this transcript
Conclusion
Best practices
00:00As a programmer, I'm very aware that for every problem there are multiple solutions.
00:05With that in mind, here are a number of best practices that I recommend
00:09for building optimal scalable solutions.
00:11One of the challenges faced as a developer is finding exactly where the bug is.
00:16Using debugging break points, there is a technique known as binary split.
00:20In short, set a breakpoint halfway through the program.
00:23If the problem does not occur, set a second breakpoint halfway through the second half.
00:28Otherwise, put the second breakpoint in the first half.
00:31Keep putting breakpoints in the middle of the halves to narrow down where the
00:34problem is occurring.
00:35Using this technique, I can limit the search area of a thousand line program in ten steps.
00:41Being able to track changes over time is essential to code organization and accountability.
00:46It's not just about blaming someone;
00:48it's about determining what changed, when and why.
00:51Regardless of the size of the project, even if there's only one person working on a project,
00:55or if there are multiple teams collaborating, use version control.
00:59For more information, I recommend Fundamentals of Software Version Control
01:03with Michael Lehman, here in the lynda.com Online Training Library.
01:08When working with a team, there are going to be bugs.
01:11It can be especially tempting to ignore problems and assume that somebody else
01:14is going to fix it. Don't wait.
01:17Act soon or rather than later.
01:18At the very least, document that the issue exists, including steps to replicate in an issue tracker.
01:24Again it's not about blame.
01:26The goal is to improve the software.
01:29The longer the delay in fixing, leads to more technical debt,
01:33which in turn makes the problem harder and more expensive to fix.
01:36Commenting code effectively is not complex or difficult, but it does take discipline.
01:41At the highest level, comments are meant to give context or execution so that
01:45someone reading the code, which can include yourself, understands what's going on and why.
01:50For example, a comment saying that a function call will be made,
01:54tells absolutely nothing to the observer. Of course, it's going to be made.
01:58On the other hand, a comment describing the functionality of a block of code
02:02helps give context to understand what's going on.
02:05Finally, don't debug in production. It'll end in tears.
02:08It's confusing to users at the very least and at worst, it's a security vulnerability.
02:13Make a private copy of the production environment, files and database for testing
02:17and do the debugging there.
Collapse this transcript
Where to go from here
00:00This course has covered a large number of tools and techniques, but it's by no means
00:04the end of the road.
00:06Execution traces also known as function traces allow a developer to log
00:10all function calls, including parameters and return values.
00:13Xdebug supports creating execution traces, which can be parsed with the trace file parsers.
00:19For more information see the documentation on xdebug.org.
00:24Instead of relying on program failures, logging or bug reports, consider implementing
00:28a suite of automated tests to fully exercise individual parts of the entire program execution.
00:34Code can be tested using a framework such as PHPUnit where tests are written
00:38that isolate the part of the program in order to show that the individual parts
00:42are operating correctly.
00:43Browser interactions and workflows can be scripted and tested with Selenium,
00:47which automates browsers in PhantomJS, which runs a functional test without requiring a browser.
00:53These automated tests can be leveraged when using continuous integration
00:57where developers' code is integrated in a centralized place in automated tests
01:01are performed to measure the quality of the code in system.
01:04This facilitates manual quality assurance practices as well and can be
01:08essential tool when scaling up.
01:10For a high level discussion of this approach, see Martin Fowler's article on the subject.
Collapse this transcript
Goodbye
00:00Debugging code is messy.
00:02In every program mistakes will be made and inevitably more problems will be created
00:06when trying to fix the original problem.
00:08My wife and I learned a valuable lesson moving into a house we were fixing up.
00:12Don't start ripping out something unless you're prepared to deal with
00:15whatever mess it is hiding.
00:17Debugging can be a bit like that.
00:18But similar to cleaning up a moldy carpet, it's pretty much a requirement
00:22and the consequences of not dealing with the problem by ignoring it,
00:25tend to be much worse than just fixing it.
00:27I appreciate your time and I hope you enjoyed watching this course
00:31as much as I enjoyed writing it and recording it while working with the team at lynda.com.
00:35Thank you!
Collapse this transcript


Suggested courses to watch next:

PHP with MySQL Essential Training (11h 3m)
Kevin Skoglund

PHP with MySQL Beyond the Basics (10h 27m)
Kevin Skoglund


Installing Apache, MySQL, and PHP (2h 43m)
David Gassner


Are you sure you want to delete this bookmark?

cancel

Bookmark this Tutorial

Name

Description

{0} characters left

Tags

Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading
cancel

bookmark this course

{0} characters left Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading

Error:

go to playlists »

Create new playlist

name:
description:
save cancel

You must be a lynda.com member to watch this video.

Every course in the lynda.com library contains free videos that let you assess the quality of our tutorials before you subscribe—just click on the blue links to watch them. Become a member to access all 98,755 instructional videos.

start free trial learn more

If you are already an active lynda.com member, please log in to access the lynda.com library.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Access to lynda.com videos

Your organization has a limited access membership to the lynda.com library that allows access to only a specific, limited selection of courses.

You don't have access to this video.

You're logged in as an account administrator, but your membership is not active.

Contact a Training Solutions Advisor at 1 (888) 335-9632.

How to access this video.

If this course is one of your five classes, then your class currently isn't in session.

If you want to watch this video and it is not part of your class, upgrade your membership for unlimited access to the full library of 1,899 courses anytime, anywhere.

learn more upgrade

You can always watch the free content included in every course.

Questions? Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com.

You don't have access to this video.

You're logged in as an account administrator, but your membership is no longer active. You can still access reports and account information.

To reactivate your account, contact a Training Solutions Advisor at 1 1 (888) 335-9632.

Need help accessing this video?

You can't access this video from your master administrator account.

Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com for help accessing this video.


site feedback

Thanks for signing up.

We’ll send you a confirmation email shortly.


By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

Keep up with news, tips, and latest courses with emails from lynda.com.

By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

   
submit Lightbox submit clicked