 I just did a presentation before this. I am not talking again. But I like being in front of a microphone as you probably can tell. So I'm going to introduce, even though he's also going to introduce himself afterwards, I'll spoiler, a good friend of mine, symphony core contributor, probably like the number two symphony guy. So if you need something done in symphony, if you don't like something in symphony, if you guys barricade the doors after the talk, you can sort of force him to do whatever you want. He's also probably one of the smartest developers I've met. I'm really lucky that we have him in the symphony community. He does the stuff that I can't figure out how to do. And then I do the easy stuff like documentation. So, yeah. So here is Nicholas Grecus and I'll leave it to him. Thank you, Ryan. So my name is Nicolas Grecus. I'm symphony core team member since two years. I work also for Blackfyre as a CTO. And I do open source since years. So maybe you know J-Squiz, which is a JavaScript minifier I wrote in 2003. Patchwork, UTF-8 also, which is now today renamed, embedded in a new project, which is symphony polyfill, that provides many polyfills for PHP. I'm also known by my co-workers as someone who writes some cryptic code. So an example is here. It's Unicode, so patchwork UTF-8. Unicode mixed with PHP. As you can see, it's able in PHP to write single character variables using combining Unicode characters. So this is a dollar with a combining car. It's legal PHP. You can run it for fun. So quickly, this is a screenshot of Blackfyre. Blackfyre is a profiler. And that's the only thing I'll tell you about Blackfyre. It's not a topic today. Today we're going to talk about debugging fluency. The goal today is to import you into using tools so that you won't get this white page again and so that you can know how to get out of this white page. So the fatal error and any other nightmare debugging you might end up into. So what is debugging? Why dumping is linked to debugging, of course. The first thing is we need tools for fixing trivial errors. Trivial means that can be detected by the PHP engine. That's a trivial error. Then when PHP says that everything is right, you have a behavior that might be wrong. And very often it's not right at the first version. So you need tools to figure out what's the behavior and how to fix it. And of course, this starts with understanding what's going on, which means representing states, because we deal with states. In the first part, I'd like to remind you how PHP works and how errors work in PHP. So errors in PHP have several levels. One is the warning level, the notice level, and deprecation. These are kind of notices. I'm sure you already saw many of them some undefined index, some F open with the permission file system issue. These are not important issues that allow, they don't break the PHP engine. You just get a notice and you can continue. But usually it may be an issue that you have to fix. And PHP is telling there might be something wrong. Then we have an intermediate level, which are the user errors and the recoverable errors. I'll tell you just after why they are special. Then we have the fatal errors. So the path error, you know that, I'm sure. The undefined function and undefined class, undefined method. So whenever you have typo, you miss some use statement at the beginning of your file. Type error, so type error is when you call a function with an array, but the function is expecting some object. You get this kind of fatal error. And the last one is the most horrible one. It's the out of memory error. And in this case, it's really, really hard to debug things. And et cetera, because I'm sure you'll find many ways to trigger bugs there. So PHP triggers this kind of errors. And PHP gives you, gives us a hook, which is set error handler with a callback. If you register a callback using this function, you will get the callback called whenever any of this function of these notices are triggered. Any of these means the first two, warnings, notices, deprecation, user errors, and recoverable errors. These are the only kind of errors that you can catch with the set error handler in PHP. And it's really useful because you can just plug what you want at this place. So what people do usually, what Drupal do, what symphony does is logging. Because after that, you can just read the log file and say, okay, I have something to fix. The function is also able to sniff, to read the error reporting level at the current moment of the call, triggering the call time. This means that you can detect and the callback can detect whenever a function is called and a trigger, a notice is triggered using the silencing operator. So you know maybe you've read about this at a thing, which changes locally the error reporting level to zero. So if you call error reporting into the callback function, you can read its return value, which if it's zero, most function, most callback, just ignore and return right now. But when it's not zero, it means it's not silent, so you can do something with that. At least the developer wants something to, wants to know about that. User errors and recoverable errors are fatal when you do not plug a custom error handler. So that's the behavior of PHP. But if you plug one, you can do something else. Then we have the other kind of errors, the fatal errors. And PHP allows us also to have some information. But then code against coaching this, so pass errors, undefined class, type errors out of memory situation, trigger the end of the PHP engine. And by the end of the PHP engine, it means PHP goes straight to the shutdown function. So we still have user-learn code that can be run at this stage. And whenever you register shutdown functions from callback, this callback will be called if a fatal error is happening during your script execution. Of course, you can do many things in shutdown functions, cleaning many things. But it's also a hack. And it's really great that PHP is so hackable that you can get fatal errors and still deal with them and do something. So typically what this kind of callback should and callback do is calling error get last, which is a function that returns an array that contains the error message, the line, and the level of the last error. In this case, it's, of course, the fatal error. Then we have another kind of error, which are exceptions. Of course, exceptions have a very different handling mechanism, and it's the try-catch block. So try, catch some exception, and do something with that. Usually user-learn code deal with them if they are expecting and if they are able to deal with not, usually not every exception, I mean the slash exception in the root namespace. But usually libraries deal with their own namespace of exceptions and are able to catch and deal with them properly. Whenever a try-catch block is missing or a try-catch block is not catching some kind of exception, and whenever this exception pops up to the root, so out of the call stack, some exception handler can be called, and it has to be registered before that with set exception handler. So it's kind of set error handler, but in this case, it's a callback that will be called whenever an exception is triggered and is not caught. So, of course, the signature of this callback is just a function that takes one argument, which is the exception. With PHP 7, we have a new kind of exceptions which are throwables and errors. So it's a new kind of object, and throwable is an interface, and error is a class, and exception, the class, is an instance of implements the throwable exception, error implements also the throwable exception, but an error is not an exception, which means that code that used the previous one, this kind of code, won't catch the error. The error will get out of the try-catch block and pop, and if you don't deal with them, and most code do not deal with them because most code are not PHP 7 aware, then the error will get up to the exception handler. This time it's the same callback, so the same callback will be called both for errors and exception, at least it could be renamed set throwable handler, but because of backward compatibility concern, they did not do that. Some code used to have a type int on the exception argument of the callback, of course PHP 7 broke that, because if you type in with backslash exception, and you get an error there, you'll get a fatal error. Okay, so another kind of error, and now we are in the daily process. I'm sure daily, maybe not daily, but very often, at least for me, we do typos, and we do character case mistakes, so when you write your class name, you miss the uppercase C, the uppercase first letter. So these are usual errors that can be worked with in the auto loader. So for that, a trick we use, and I will present you, is to decorate all auto loader, all functions that load your classes, and by decorating them, we can add checks and checks that verify that everything is good and I'll show you. You'll see. An example is this one. So the last one is you can get a message saying calling, you're calling undefined method foo. And by looking at the message, you can know that the class is bar. So the message says calling undefined method bar on class, commit of foo in class bar. And in this case, we can hook into this and say, okay, let's inspect by reflection the bar class and look at all the existing method and by Levenstein distance say, okay, this must be a typo and the likely good method is this one. And then you can have very user friendly error message suggesting the correct version of the method in this case. So that's for general error lifecycle in PHP. Now, let's see quickly what happens in let's start with Drupal. So in Drupal, by default, notices PHP notices and PHP warnings are just logged. The error handler and the exception handler, if you look at their implementation, they just call the logger that is inside of Drupal. So by looking at the log, you'll see just the error and the notices warning that happened. And code exception trigger the kernel exception event. So this means that the core of Drupal, in fact, the core of the symphony kernel has a try catch block and the try catch catches exceptions. And this exception is forwarded to an exception to the event dispatcher and the event dispatcher triggers this kernel exception event. And I think the default is to cast the exception to a string. So just displaying its message and the stack trace and then return a response with this body content. So that you have, I'm sure, seen this page, which is quite white with the exception message and the stack trace behind. In symphony, as so also in Drupal, the PHP 7 error throwables are not caught by codes by the kernel class. It's not, we don't deal with them in symphony. So they end up calling the set exception handler callback. And the default one is calling the logger. Same as before. In symphony, we have kind of different defaults. Notices and warnings are converted to exceptions. This means that if you use some undefined variable in your symphony code, you'll get an exception. So you will have to fix it to continue developing your controller or anything. In Drupal, it's not the case. And so it means you kind of have undefined index and defined variables, things like that. And PHP can deal with that. And it won't tell you anything except in the log. And execution will continue. So that's another way to develop. And code exception trigger the kernel exception event that the same wiring system. PHP 7 errors, same thing. And there's difference because whenever some code exceptions end up in the error handler, in the exception handler, then they are re-injected into the kernel, which is something Drupal does not right now. But by having this, we are able in symphony to take fatal errors and deal with them as if they were exceptions. Which is really useful because we can then reuse all the nice displaying tools that we have for exceptions. Tools and logging. And we have about logging, we have another option, which is to log all errors, even the silenced one. Because it's really useful to know what's happening just in your application. And just because some developer added an at scene before some F open call or something like that, doesn't mean that there is anything to look at there. And maybe the bug you're looking for is just silenced by that. So what we do is we log it. Of course we can't trigger an exception. We don't want to break the code at this stage. But if you look at those logs, you can have more information and you have full information, in fact, with these settings. Okay, so symphony, as you know, has several components. One of them is the debug component. The debug component is already in Drupal 8, but it's not used. It's already in Drupal 8 because it's a dependency or the HTTP kernel component, which is used by Drupal. Which means you don't have to do anything to use it. Only write code. What this component does is it takes all the PHP error levels and whenever one is triggered, it's just map these notices, warnings, anything, to the PSR3 logging system. So do you know PSR3? Yeah, who knows? Okay, so PSR3, so the PSR, PHP standard recommendations, which are standards that are used by symphony, by Drupal, by many other projects that define interfaces. And one is the PSR3, which defines some interfaces to log, to log any message. So there is a method that is called log, another one which is called warning, another one which is debug, so it depends on the level. You have several methods, one per level. And what this does is it takes a PHP error and it maps that to a logging level error. Which allows them to reuse any logging system that is PSR3 standard. Another thing that is done by the debug component is converting PHP errors into exceptions. I told you about that because this is what symphony is using by default. So any error is converted to an exception. This is an option. So you can configure and get logging or throwing. Because just before I told you there's mapping, when you can map, of course, PHP errors to exceptions, which means no logging, because you have the exception. There's no need to log that. The error handler mechanism of PHP allows you to have access to local verse, to the local scope. And so it means local variable that's where defined at the time the error was triggered. So with the error handler, we are able to get them and log them and put them in the exception what you want so that you can know what were the states in the local method that triggered the exception, triggered the message, with the stack trace also. So you have the message and you know how PHP ended up in this situation. We have unsilenced configuration to tell to the logger or to the debug component which error should not be silenced. I told you already about that and we use it in symphony. And it's able to turn fatal errors into exceptions. So this is done, you know now how to do that. You register a shutdown function and in the shutdown function you get, you call error get last and with a returning array, you do something like instantiating an exception and forwarding it to something else, somewhere else. Especially you can re-inject it into the logging system. So that's the job of the error handler. We have another utility class which is the exception handler, the exception handler has a job which is to generate HTML by taking just an exception instance as argument. You do an exception handler, handle exception and it displays this kind of HTML pages. The other thing, the other useful class is the debug class loader. So the debug class loader has three main use case. One is to stack compile time error. So this is really an edge case in PHP where some errors can't be logged properly at some stage which is at some compiling stage in fact. So you don't have to know full details about that but the debug class loader allows you to not end up in a trap which exists in PHP which is this one. Another feature is to force unsaliencing of past errors. I'm sure or at least I did one day have a white page without any error message and this was because after several hours this was because of some at scene in front of an include. So the at scene set says just don't log any error. This is really, really bad when you have some past error, some fatter error in the include in the full PHP file because you have the error you said to not log it so you don't have, you have the age closed and you can deal with that. So what the debug class loader does is that it changes locally the error reporting level and it forces past errors and some other kind of errors to be unsilenced so that you will get them and you'll see them and I'm sure you never want to silence them. Another example and maybe the last feature of the debug class loader is sniffing and trying to find case mismatch between loaded declared class names and the fine name in which you put your class declaration. On Linux this doesn't matter very much because on Linux we have a case sensitive file system so whenever you do some typo you will get a fatal error saying okay, but I'm sure many of you have Mac OS and Mac OS has case insensitive file system and Windows also have a case insensitive file system and we added this to the debug components because one day one of our developer wrote something and we pushed it to prod and we broke the prod because he was on Mac and he has a typo. So now, fortunately, we have this and this is able to check that this won't happen again. It won't happen again to you also now. So case mismatch between loaded and declared class names trying to load a class with an invalid name this is really a raw edge case. Fine, the file was found but the class was not in it so you look for food.php we find food.php, we load food.php but there is no food class in food.php. What does that mean? So maybe you're missing the namespace, something like that. And then we also have the case with case mismatch between class and file name. A last feature is that the debug class loader is able to inspect the method that you're calling the class that you're calling and the interface that you're using in your code and it's able to warn you by just adding a message in the log system to warn you about deprecated interfaces, abstract classes that you are using. This may be not useful right now but one day you will have legacy code and one day this code will be deprecated by the main author, some library and so you will know how to fix your code because the code will tell you, this is deprecated, you should use that now. Okay, let's change component. This is the second component. So the Vardrumper component is about displaying the state of any variable. So the mission statement of the component is to generate some easy to read HTML or common line output. The second feature, mission of the component is to provide an accurate, very accurate state representation of any variable, an array of float and int. We don't want to represent one and one dot zero in the same way so that you can know that this is a float, this is an int and you have the hint to understand the very accurate state of any PHP variable. PHP proof, this is also very important because PHP is full of edge cases and when you are debugging, you are in an edge case, usually. So it means that the component is not allowed to break and we try very hard to test it in every crazy situation so that you can dump in any situation. And then, last but not least, we want it to be extensible and reusable which means that I will present you the components and you will see it from the outside and the outside is one feature to represent the state, so to dump, it's just a valid prepressment. And internally, it's an API and interfaces and the classes that allow you to just build something else on top of the core algorithm and core interfaces so that you can extend it and provide some other representation, anything you want to build on top of that. Okay, let's do some examples. So this is a simple array. So very easy to understand, I guess. So we have five elements, a simple string in an array of five elements, a float, an integer, a boolean, an empty array. As you can see, it's pretty straightforward. This is a kind of complex array. So this array has internal references which means that some indexes are just aliases to other indexes. And in this case, we have the index zero, index one, and they both hold the same value, but in fact, not because they're the same two at two different positions because it's the same position. Both index are a reference of each other which means that if you change index one, you will change index zero. So this is displayed with this E, the 1% one, and we have the same representation just for an array. So in index two, index three, we also have the an alias, and in this case, we put an array there. So you can see that we opened the first one and we collapsed the last one. So this is something that you cannot know currently with tools, displaying tools, and sometimes you will have a bug, one day you will have a bug, and the bug will be related to this, and you will need the tool to see what's the data structure that you're using, or maybe someone made you to use. This is a simple object. So a simple object has one public property. So with a plus prefix, so plus means public, the sharp prefix protected property, and the dash prefix for private properties. This plus sharp and dash is a convention that is used in UML. So if people, some of you are used to that, you should be familiar with that. We could have invented something else, but we shouldn't. When there are standards, it's better to make everyone aware of it and so that people raise their knowledge. Okay, so let's talk about internal things. So how does this work? We have a variable, which is doll over. First, the first step to get this kind of representation is to take a var cloner object, which is something that is implemented in the var dumper component, and to use it on our var variable. The output of this call, which is, there's a method which is called cloner var. So whenever we do cloner cloner var of vars, then we get back data object. Data object is again some internal thing and it's representing the exact state of the var variable, but it's not the var variable anymore, it's something else. Then, what the var cloner does inside internally is that it uses casters to extract accurate representation about all your custom objects. So this means that if you're using doctrine and you don't want your entities to be dumped with the entity manager, which is a very huge object, then you can write a caster and the caster will have some implementation saying, okay, this is an entity. I don't want the entity manager to be dumped because I don't care about it. Let's remove it from the representation and then represent it. And what the var cloner also does is that it enforce state extraction limits. This just means that we can have a max level, have a max items, max string length, so that you don't dump the full PHP state but you can limit your representation to the first levels, nesting levels of your objects. So the resulting data object from the cloning of the var variable is basically internally just a simple PHP array that holds full precision over the state of the initial var, which means that it's just if you remove all the limits, it's an exact representation of the variable that we are going to dump and you can serialize it and put it on a file. You can store this object in a database, do something else with it, forward it to some remote server, it has the state and you will be able to represent it later in the process if you want. So there is a separation between extracting the state which generate this and then dumping, representing this thing. So then this is done, this job representation is done by what we call a dumper object. So we have two, a key dumper, so command line dumper and HTML dumper, maybe your dumper, you can write one. And this one turns the data object into a string representation. A string representation is what is displayed on your screen so HTML command line thing. And then this string representation is written to any PHP stream or any line callback. So you can hook into this and say, okay, I don't want this to be printed on the standard output on the output of my web page. I want this to be output somewhere else, maybe in a file, maybe on the standard error stream, maybe on the network protocol stream, maybe on some callback. So callback will be called for every line that is generated for the representation so that you can do anything, any wiring you want with the output. So this is another example. This is what I call a complex object. Why is it complex? Because it's, so in this case, it's a redis object. So redis is the database and the redis object is PHP driver implementation that is able to connect to redis, the same kind of, it's database connection, redis database connection object. And usually this is provided by the redis PHP extension. And if you dump this using Vardom, for example, of using printer, you will get only the first line. The first line will tell you, okay, this is a redis object and it holds one public property which is socket, which is a redis socket buffer resource. By using a caster, and this is why I put this example here. By using a caster, you can add useful information so that you have more information when you're dumping this object. In this case, we have a redis connection and on the redis object, even if it's not an internal property, private or public, there is a state. There is a state and the state can be read by using some is connected method, get host method, get port, get hot, get DB number, get timeout, get persistent ID. So if you're working with redis object, you have to know that you can inspect the state of the redis subject by using this method. What you can do with the Vardom prequipment is code that knowledge into a caster and then the caster will just know that for you and whenever you end up doing dump from some redis object, you will have this representation. So this representation is redis aware. Enjoy. This is the implementation. So the implementation says basically that it's constricting some array as you can see and the array calls all the interesting function on the redis object so that then the representation is generated by using this array. That is constricted there. So this is just a public static function which means there's no state, there's no object. It's really simple. If you know, you maybe need to read some documentation about that to know the exact meaning of the arguments but anywhere as you can see, there is some knowledge here which is how to access the state of the redis connection object. And we have many of them and we add many more over time because this one is very new. The previous one was maybe a PostgreSQL connection displayer so we now have a nice display for this kind of object and so on. Okay, this is the kind of code you need to do if you want to dump by hand. So this is the implementation of what I tried to explain you before. So at link four, we create a cloner, so a new var cloner. Then we create a cleadumper. Then we pre-press some stream output, so some PHP memory buffer and let's go in on line eight, we call cloner, cloner var of some variable and then the output of this, the return value is data object. We give this data object to dump per dump and dump per dump will generate the string on the output that is the last argument there. Then you get the output when we stream get content on the output resource and that's it. You have a variable with the string representation of the variable. Okay? Okay, let's practice, what time is it? Okay. It's a lot simpler that one. You may have. The first thing, so symphony var dumper is just a standard composer package that you can require. So you do composer require, symphony var dumper and that's it. That's it. It works on Drupal 6, on Drupal 7, Drupal 8 and symphony on in fact any PHP application. This means that when you, composer require symphony var dumper, you will get automatically a dump function. So then you just dump some variable, dump and that's it. You get the representation. This is able to automatically switch between HTML or CLI depending on the runtime context. You can also install that using composer global require and using also some auto initiating configuration. If you do composer global require and then auto prepen file in your PHP file you will get dump all the time without doing anything on all your project. It will be installed globally on your laptop. Okay, let's try. So here I have a small Drupal application. Okay, this is standard directory listing. I have a demo PHP file. Okay, it's empty. Can you see? We'll see this one is, okay. So we need the auto loader of course to load our dependencies and then let's just dump. Okay, one, two, three. That's it. We are not in a Drupal application. We are just in the bare minimum PHP script. So we can try any variable here. So of course an array. Here's our array. And we can do some Redis connection. Redis, then I have a Redis server running there. So let's connect some local hosts. We have a connection object. This is the representation that you saw on the slide. But now it's live. Okay. In a Drupal application, you can use this and you can enable everything I told you about by changing your settings local PHP file. And if you add this at the end of the file, you will get the debug components enabled so that you'll get error reporting in this kind of symphony way. So let's do that. So I think, okay, the Drupal server is there. It's just Drupal server running. Okay. I have my application there. So, okay, this is a BANYET application. And so the BANYET application has, let's start. Okay. So here is a controller which, okay, it's an usual controller for Drupal application. And in this case, let's say I want to dump my BANYET. Let's back. I didn't enable the debug components right now. Okay. I'm back to demo time. Okay. So we have an error and define variable BANYET. So this is okay. This is because I plugged, in fact, everything. So this is the error page that I plugged with the dump function so that we dump whenever we have an exception so that we have more context and maybe more friendly display. Let's see this one. So in this one, we have some noticing and define variable BANYET, which obviously has a typo. We have some context. We have a number variable which is called number. I guess in the code we'll see that there's a number variable. And we also have one which is BANYET, which is the one I was trying to dump. And it has a strawberry. It is strawberry field. Can you see there? Maybe I need to zoom. Okay. Much better. So let's continue because we have more information there. We have a stack trace. So the stack trace shows us the list of functions that have been called up to our controller. So if I go back at the end, in the main function, which is the abstract root node, and then this index PHP file, in fact, is calling Drupal kernel handle. And you'll know that because you maybe have already opened the index PHP file. And this one is calling the stacked HTTP kernel handle and so on. So this is a typical Drupal call stack. We end up triggering this exception with the codex there showing that this is the line that is triggering the exception. Okay. And we can inspect and get all the lines and the arguments. So dump and then we had one argument which was this one because we are still in the error handling part. And we can continue and see there that we have, okay. Drupal is doing many things there with symphony help and we have many things. So let's fix that. But in fact, we already saw the value and I know it for that. What else? Okay. So let's see this one. I don't need this. I'm in my settings default. And then the settings local file. So in this file, I have this which enables the debug class loader so that whenever I do some typo, I will get the right message. I have the exception handler and I have also the error handler and this is why we're getting this strange error page with the black background doing everything. Okay. There's also something else we can do. Maybe this is just advanced and this is just to show you that you can also plug everything as you want. In this case, I plugged an event subscriber for the kernel exception event because I told you that whenever an exception is triggered and is not dealt with by any code, then it will end up in the kernel exception event and this is something you can listen for and in this case, I have one listener. So this is the part, let's forget about this one, but the first lines. So this is the service declaration for our exception subscribers. So we say whenever you need to, okay, if you need to register the event, let's look at the exception subscriber class and this one will tell that we'll deal with this. Okay. Let's go. We have the class here and the class is just doing this. Sorry. So this is the implementation and this is an event subscriber and the event subscriber has the bottom function is just a static declaration function saying this class, the own kernel exception method should be called whenever a kernel exception event is triggered. So that's the last part and the first part is the function that is called whenever an exception is triggered and in this case, what we do with the exception with the event, in fact, the beginning is get the exception that is inside the event and just dump it, the same dump function that we used before and we set the response with a new response which means blank page because dump already output something so we don't want anything else. Okay. So this will display a very nice page, nice in term of this kind of page. If we have a few minutes and a few minutes of batteries also, we can write something else which is some, let's do our D, D for debug, D function, let's do it together. So I want my D function to output somewhere else. So let's create a cloner. So this will be a new var cloner object. Let's do some clidumper because you'll see what I want to do later. So when doing a clidumper object, then okay, let's forget about indentation because it doesn't work. Then let's get our data from the variable. So it's cloner clone var from var. Okay, so now we have the data object and let's dump it. So clidum from our data object and this is where I want something special to happen which is that I want the output on the standard error of our PHP process. There's no pass error there. Okay. Let's use it, use it. So let's do the controller. Nothing there, but here it is in the log of our web server. So we didn't alter the output at all and we have some console output even though we are in the HTML context. So what's next? Maybe some native Drupal integration because some wiring are not that easy. Maybe a var dumper module. There is one already existing. I tried it, there's still work to do. Don't wait after others which means you can write the code because it's an open source project and Drupal is and you already know how to use dump, just ComposerRequire, SymphonyVarDumper and happy then. So if you have any questions, I'm here. Just come to me. Yeah? It's really good. Thanks. There are a couple things there. Yeah? It's great. It's awesome.