Imagine writing a book with a team of other people, but you're not following the same grammar and spelling rules. It would be really difficult. In writing prose, grammar and spelling are our standards. In writing code, we have standards as well. Following these rules helps us maintain code together.
Coding standards help us agree on the level of quality our code should meet. The Drupal community has a thorough and ever-evolving set of coding standards to keep our code readable and maintainable by developers around the world - this allows Drupal's open-source community to thrive all over the globe.
Even if you aren't contributing code back to Drupal (gasp!), and you think you are the only person who will ever see your code - you truly never know. Best practices exist for a reason, and you can never be sure where your code will end up, whether it will need to be scaled, or who will read it one day. Keep it standardized, and you'll never have to worry!
If you’re new to Drupal, this session is essential! If you want to want to brush up your knowledge and clean up your code, or make sure you're writing clean code in Drupal 8, this session is also for you.
You will learn the answers to some important questions, such as:
What are Code Standards? Why should we follow Code Standards? Why are they important? What do they help us achieve? Who decides Drupal’s Code Standards? How do we implement them?
You will learn both formatting standards for your code and documentation standards, as well as some specifics for things like Twig, and object-oriented programming in Drupal 8.
You will learn how to implement these standards using tools like Coder and PHP Codesniffer, and how to make your editor do some of the work for you.
This session is appropriate for any level of Drupal experience, from beginner to expert.
Transcript
Alanna Burke: Awesome. Hi, I'm Alanna and this is Code standards. I work with a company called Chromatic. We've got some swag up in the front. There's also a few of us here, we've got T-shirts. If you want to talk to me or one of my coworkers at the end of the talk, we'll be happy to hook you up with one of them. We're a pretty small company. You can find us at chromatichq.com or @ChromaticHQ on Twitter. It's a little cut off there. You can find me @aburke626 on Twitter or Drupal.org.
Here we go. Code Standards: It's okay to be yourself but write your code like everyone else. Just a note, there's a lot to cover here and a lot of links. I'll make these slides available right after the talk. I'm going to go pretty quickly, so don't worry about taking notes or trying to write down the links. I'm also going to make sure that you get the links to the series of blog posts that I wrote which inspired me to do this talk. Some of them go a little more in-depth and gives some more examples as I only have so much time here.
What are coding standards? Standard, a level of quality or attainment. I find it useful to divide standards into two types; style, which includes things like indentation, spacing, line length, and substance, which includes things like correct use of functions and components. Why are they important? In a blog post from 2004, PHP developer Paul Jones writes, "The point of a coding style standard is not to say one style is objectively better than the another in the sense of the specific details. Instead, the point is to set up known expectations on how code is going to look."
Code standards are important for a lot of reasons. They keep your code readable. You can spend less time worrying about what your code looks like and more time coding. It helps bridge the gap between coders of different backgrounds, languages, and locations, which is especially important in open-source software projects such as Drupal where developers from around the world work on the same code. It's also important for automation and will get into this a little more later.
Who decides coding standards? Well, we do. Standards are decided by the Drupal community. How are they decided? Drupal standards are based on the PEAR coding standards and they're decided in an issue queue on drupal.org. If you're ever interested in checking that out, there are a ton of people who get really into it. It's fascinating to see how this little tiny minutia can result in these huge discussions but eventually, someone decides on it and it becomes a standard and it goes into the documentation.
Now that we're gotten some basic questions out of the way, let's talk about how we implement coding standards in our projects. The first thing that I have to recommend is to read the coding standards and keep them handy. As Drupal's code is changed and updated, so are the coding standards. They're like a living document. Make sure that you bookmark them and periodically review them for changes and updates. You can find them at drupal.org/coding-standards.
The next thing is to set up your editor for success and let it do the work. There is a lot of documentation out there for using editors specifically with Drupal, especially for Sublime Text and PHPStorm, but other editors as well. Look for this, set up your editor, and save yourself some work. One of the easiest ways to implement coding standards is through view your own code with the coder module which implements PHP_CodeSniffer specifically for Drupal. Here's an example of running it with the shortcut drupalcs on a file in the countries [unintelligible 00:04:24] module and getting a list of errors back so I know what to fix.
There's also a link here to a great post by one of my coworkers about enforcing coding standards with PHP_CodeSniffer. Team code reviews are another important way to enforce code standards. You have to make the time for them. The most successful teams build in time to review one another's code. There's no substitute for code reviews by another person. Make sure that you view them as an essential part of your process. Yet again, a link by one of my coworkers to a blog post explaining why code reviews are worth their weight in gold. It's really important for us at Chromatic.
When reviewing code whether it's yours or whether it's someone on your team on drupal.org or on GitHub, make sure to remember these two things. Treat others as you would like to be treated. Be kind, courteous, respectful, and constructive. Be aware of your tone. When we're typing things online it can be really hard to get your tone across, so reread what you write. Take everything in stride and don't take it personally. Those reviewing your code want it to be good and corrections aren't a personal attack.
Next, let's talk about formatting. If you watched the show, Silicon Valley, you may recall the episode where Winnie and Richard break up because she uses spaces and he uses tabs. In researching this talk, I found that, yes, people do feel that strongly about it. Lucky for us Drupal folks, it's already been decided, two spaces. This is really easy to set up for example is Sublime Text. You don't have to worry about it.
Some other whitespace standards. No trailing whitespace. This means no spaces or tabs after the end of a line. This can also be set up in your editor. Use blank lines sparingly to keep crowded code readable if necessary, but try to avoid extra blank lines throughout your files and functions. When it comes to file endings, Drupal uses Unix file endings. If you're not aware, the difference between Windows and Unix file endings is just what characters are put to indicate that it's the end of the file. There must be a single blank line at the end of each file. This is another thing that most text editors can do for you.
Next up is arrays. In Drupal, we have some new short arrays syntax standards that are specifically for Drupal 8. They can also apply to Drupal 7 if you're using PHP 5.4 or higher. Essentially the new short arrays syntax replaces the word array and the parenthesis with square brackets. You can see in the example of the new one, we've just got square brackets around our array items. The old arrays are wrapped in parenthesis with the word array. Just be aware of this. It's not supported in versions of PHP prior to 5.4 but you will be using it in Drupal 8.
Line length in arrays. If you have an array declaration that's longer than 80 characters, split it into a multi-line array, like this. This example is also using the new short array syntax. Multi-line arrays work the same way whether you're using the old long arrays syntax or the new short array syntax. Each item is on its own line. Each item is followed by a comma including the last item. This is Drupal best practice regarding arrays in PHP.
Somethings to think about. If you have a super long array that's hundreds of items, you could break each item into its own line. That would be very very long but very readable. If the programmer who looks at this code next is unlikely to need this info at their fingertips, maybe it's a list of ZIP Codes or states or something like that, consider importing it from a CSV file or similar and keeping it out of your code.
Now let's talk about line length in general. Lines should be 80 characters long. If breaking up your code over multiple lines however makes it less readable, reconsider. The goal here is readability. Comment and documentation text, however, should always be 80 characters and under. Here is an example of how easy it is to set up a ruler in your editor. This is Sublime Text.
Operators, we use these all the time. There should always be one space around operators. Here are some examples. You do not need spaces just inside a parenthesis. To show why we put spaces around operators, here is an example without the proper spaces. Using the ternary operator, in general, can be hard to read in the first place. Without spaces, it becomes much harder to see what's going on. With spaces, it's suddenly much clearer. We can see that this is saying if a = system or b = system and then the ternary in the return statement is saying if a = system return -1, otherwise, return 1. It's so much harder to figure out in that first example without the spaces what's going on.
Function calls and declarations. When declaring a function, there should always be a single space after the argument list, and before the opening curly brace. The function then begins on the next line indented with two spaces. The closing brace always goes on its own line. A function call has a set of parentheses with no spaces on either side of them, whether or not there are parameters. If there are parameters, they're separated by a comma and followed by a space. This is all pretty basic, but we want to explain it.
This is an update hook from the Advanced Forum contrib module, the simple example of both a function declaration and a function call. Here we're declaring the function advanced _forum_update_7200. You can see there's a set of parentheses with no spaces before them, one space, and an opening brace. Inside the if statement, we can then see the function call to variable set with no space before the opening parenthesis, the arguments separated by a comma and a space. Again, basic but it's good to start here.
Constants. Notice the all caps in the previous slide are NULL and FALSE. TRUE, FALSE, and NULL are always capitalized in Drupal code. Constants always all caps.
Custom constants must be prefixed with the module name. Here's an example from the CKEditor module. Each of these defined constants is prefixed with the module name, CKEditor, and is in all caps.
When using control structures like if, else, elseif, case, switch, foreach, while, do, et cetera, there should always be a space after the control structure term. This is one that I see mistakes with a lot because a lot of languages do this differently. There should always be a space before the opening curly brace. The statement is indented on the next line, and the closing brace is on its own line, much like functions.
Inline control structures are not permitted in Drupal, although they are valid PHP. You should not use either of the following structures in Drupal. First one everything is on the first line with no curly braces. The second one, I put the statement on the second line, but still no curly braces. These are both valid PHP, but they're not using Drupal standards. Control structures must always have braces, and the statements must always be on the next line.
Note that in Drupal, the standard is to use elseif as one word, not else if as two words. Again, both are valid PHP, but the Drupal standard specify it as one word. We use an alternate control statements syntax for theme templates. Use if with your statement in parentheses, a colon, endif on the next line, semicolon without braces. I'll show an example on the next page. Statements must still be on their own line as much as the endif statement. Here's an example. We've got our if statement. On the next line, we have this code execute. Then on the last line, we have the endif and a semicolon. This is only valid to use in theme templates.
Casting. This is something that I feel like I don't use a lot and yet I run into these errors pretty often. The type is always wrapped in parentheses. You always put a space between the type and the variable. We've got an example here from the Big Menu contrib module. We're actually casting this variable twice to an int and then to a string. Note that there's a space after string and again after int.
This one you probably know but it's so important it gets its own slide. Every PHP statement ends with a semicolon always. PHP tags. All PHP files begin with an opening tag, but never ever use a closing tag. We also never want to use PHP short tags in Drupal. Why? Because whitespace or other characters after a closing tag can cause errors. Allowing PHP to close it on its own eliminates those errors.
Now we're going to talk about documentation. Why is documentation important? It tells us what to expect from our code, much like code standards themselves. I know what my code does, you're thinking. Sure you do right now because you just wrote it. How about two months or 10 projects from now? Will someone else know what you meant? The key is to make your code maintainable by everyone. Of course, ideally, you're writing code that's easy to comprehend because you did everything in a perfectly logical and straightforward way but comment it thoroughly just in case.
You might also do something clever to solve a tricky problem, but to the next person, it could look like a mistake. Documentation can help clear this up. My code is so good, it's self-documenting. Of course, it is. Even to the best programmer, documentation is still quicker and easier to read than code. Documentation is especially helpful for beginner programmers. Say someone who's just starting out with Drupal and PHP, who's looking through your code to see if it does what they need. If you can spell it out for them, you'll help them figure out what it is they need a lot faster.
Thanks to Drupal documentation standards, we have a few ways to document your code that are designed to be quick and easy for the developer writing the code, and the developers reading and modifying it. That said, try to avoid repetitive inline comments. You don't need to comment on every single line of code, especially if it's very clear what it's doing. Comment on functions, on large blocks of code, things that may change. Try to think about what might be unclear to the next person who looks at it.
Doc blocks. These are specially formatted blocks of information that go both at the top of each PHP file and before each function. File doc blocks go at the top of a file to give you an overview of what the file contains. There should be one blank line before the opening PHP tag and the doc block and the first line should be file. On the next line after the file tag, we have a short description. The file doc block may also commonly include author and version tags.
Here's a great example from the Backup and Migrate module. It's short and simple. It explains what to expect in this module file. Each function will be documented separately, so this doesn't need to be extensive. We have the PHP tag, a blank line, we have the file tag, and then we have a two-line description. In something like a template file, you'll see more detail in the file doc block, because the rest of the file may not have as much documentation. The file doc block may often spell out all of the available variables.
A function doc block goes just before every function in every PHP file. There are no exceptions ever. Only a one-line description is required and you should include any parameters and return types. Here's a great example. Now, a function doc block with only a one-line summary will pass coder, but it's lacking important information. Now, does this take a little more time? Absolutely, but I guarantee that you will have a better understanding of your code if you write out the documentation in as much detail as possible.
Now, we know what's passed to the function, what each variable is, and what the function returns. Good documentation can also aid in debugging. If the documentation doesn't match what's happening in the code, you know something's wrong, and you've got a starting point for fixing it. There are a variety of tags you can use in your doc blocks and they go in a certain order. We have a one-line summary ending in a period, additional paragraphs of information, we have a var tag, param, return, throws, ingroup, deprecated, see, todo, and then we have plugin and some other annotations.
Groups and topics are tags that come from Doxygen on which these standards are based. It helps to group generated documentation for the API module. The deprecated tag can be really useful if you don't want to delete a function, but it's no longer the most up to date and someone could have custom code that relies on it. Each type of tag should be separated by a blank line. The most used tags are probably param and return.
Here's an example of how the countries module uses some of these other tags. Here the description tells us that they're generating a country form which is ingroup forms, and then they tell us to reference see these other functions which are countries_admin_form _validate and countries_admin_form_submit. When you're implementing a hook, like hook_menu, all you have to put is implements hook_menu. If you put more than this, coder will give you a warning like format should be implements hook_foo. It's also going to tell you a lot of stuff that is technically not always correct, but it's going to yell at you.
Don't duplicate function documentation. You will get a warning like hook implementation should not duplicate param documentation. It's really important that you document something that is changed in your hook implementation. Don't skimp on the documentation, but you can always put that documentation inside the function if you want to keep coder happy about the document doc block. Now, why are these doc blocks so important and why do they have to be formatted so exactly? The API module parses the information in doc blocks into human-readable documentation. All of the documentation found at api.drupal.org is generated this way.
Let's talk a little about inline comments. Drupal generally uses C++ style notation with two slashes, C-style comments with the slash and a star are allowed, but they're discouraged within the functions. Inline comments should not follow a statement, this means they get their own line and inline comments must always end in a full stop. Always use a period, an exclamation point if you're excited about it, a question mark. They must never be longer than 80 characters. Also, it's usually understood that a comment precedes the line that it's commenting on, but you can always make this clearer by saying something like the following line does X, Y, Z.
Finally, something that I only found out when I was researching this is that Drupal has a style guide for content, it's style of various industry-related terms, like how the word Drupal should be used and what should be capitalized. You can find this at-- and this link is not wrong, it's just weird looking, drupal.org/drupalorg/style-guide/content. Again, I'll post these slides.
The next thing I want to talk about is the translate or t function, which is a very widely used and misused function, so much so that I think it needs its own section. What does it do? It translates a given string to a given language at runtime if you have more than one language enabled and this allows for localization, you wrap all of your user-facing strings in this function so that they can be translated, and depending on which placeholder you use, it runs different sanitization functions.
When and where do you use it? Pretty much everywhere. Every user-facing string. This ensures that your site can be localized. When in doubt, translate everything. You might be coding and thinking, oh, the string will never be translated. Two years later, when you're trying to find a string that's showing up untranslated on the site and your stakeholders want it changed, you will be much happier if it's already ready to translate, and I'm speaking from experience here.
The parameters for the t function, there's three of them, two of which are optional. The first is the string to be translated. The second is an array of replacements. The third is an array of options. The options array isn't often used. Let's dig into it a little bit. From drupal.org this is an associative array of additional options with the following elements; the langcode which defaults to the current language. It's the language code to translate to a language other than what is used to display the page. The context, which is interesting, is a string giving the context that the source string belongs to.
What is this? String context is a way to organize translations when words have one too many translations. Each original English string can only have one translation, which makes sense. This is a problem when an English word has several meanings like order, which can mean the order of elements in a list to order something in a shop or an order someone has placed in a shop. For many languages, the string order needs a different translation for each of those meanings. This is where you would use string context and you can read more about actually implementing it on drupal.org but I wanted to mention it here.
Placeholders. They come from the format string function, which is called by the t function. The most common placeholder is probably @variable. This placeholder runs check_plain on the text before replacing it and you never want to pass a variable through T directly, only string literals. Now the short explanation for this is that the string to be translated needs to be available at runtime and the variable may not be available and may change its value. I found a really in-depth explanation on Stack Exchange. This was a really great discussion. If you want to check out this link later, I highly recommend it.
How do we use placeholders? You use it to insert a value into the translated text, like in this example from the Advanced Form contrib module. Here we see the @forum placeholder inserting the forum name value into the string. We also have the %variable placeholder. This runs drupal_placeholder on the text, escapes the text, and formats it as emphasized text.
There's the !variable placeholder. In Drupal 7, this inserts your value exactly as is without running any sanitization functions. You would never want to use this on user- entered text and only on text that has already been properly escaped. In Drupal 8, ! variable is deprecated as there's no longer a placeholder for unsanitized text because it's not really a good idea.
New in Drupal 8 is :variable for use specifically with URLs. In Drupal 8, :variable is escaped with this function, Drupal\Component\Utility\Html::escape, and it's also filtered for any dangerous protocols using URL::HelperstripDangerousProtocols. This is a great way to make sure that any URLs you are using are nice and safe in your code.
When don't I use the t function? In Drupal 7 there are a couple instances when it's not available. During the installation phase, T isn't available, so you have to use this get-t function. You can do something like this. You set a variable to get-t and then wrap a string in that, it’s little awkward. Translation is also not used inside of a hook_schema or hook_menu, but in Drupal 8, it's available all the time, so you can always use it.
The next section, I'm going to show some bad examples of using T and links, because this is one of the places where I find people get the most confused in how it should be used. There are a lot of ways to do this and a lot of them are really over complicated that I've seen. These are all abstracted, but these are all actual examples that I have seen, I just rewrote all of the words in them. Don't concatenate T strings around a link like this. That's just a really strange and silly thing to do.
Don't use a variable to insert the entire URL and its HTML markup into the text. It's just weird and you could probably use the L function instead or there's a couple of different ways you could go about that. Also, don't insert the entire thing directly into T, you would want to do something, especially in Drupal 8, you would want to do something with that URL.
I've actually seen this, don't insert the L function and the t function. You could use one or the other, but using them together like this, very awkward. It's just redundant and over complicated. Good way to do it. Use the t function and a placeholder to just insert the URL into your string. This is good. This is simple. Here's an example from Drupal 8 Core using both %variable which we recall runs Drupal placeholder on the text, escapes it, and formats it as emphasized text. Then using :variable on the URL which strips the URL for dangerous protocols.
We can also see that the link markup is inserted into the t function, which is okay. If you can avoid putting any markup into the t function, that's great, but not at the expense of making overly complex and difficult to translate chunks of code. It's better to just put a little HTML in your t function if you can’t avoid it. Some best practices. Writing your code and content to be translatable.
It isn't just the best practice, but it may very well be used to actually translate your site. Sometimes you need to think from the point of view of a translator. Try not to abstract out your pieces of content too much. For an example, if you had a blog title, "Bob's homepage," your instinct might be to abstract it out like this, the username concatenated with an apostrophe S, and then the translated homepage. What's the problem here? In other languages, this phrase might be rearranged. For example, in French or Spanish, it would be "Homepage de Bob." This example that we have would require a translator to change code. What's the solution? Less abstraction. Translate this whole chunk, user, with placeholder user's, 'S, Homepage. This can easily be changed without coding to, "Homepage de placeholder user," and then we just insert the username in here.
This doesn't require the translator to change any code at all, just rearranging the words. In the example in the previous section, we showed where concatenating a translated string with another string can make some trouble. There's some other things you want to avoid. Both of these examples could be avoided by using one t() function, and placeholders, and no concatenation. Again, these are things that I have seen trying to concatenate strings within T instead of just having all of this wrapped in T with none of this concatenation and just having that one replacement. Then, the second one, you absolutely don't ever need to concatenate T strings and variables. You can just replace mystring with a placeholder. This would also give you a codesniffer error, because you should never have leading or trailing whitespace in a translatable string.
So, do this. Here's a simple way to join mystring and translated strings by using a replacement. This is how the t() function is designed to be used. In Drupal 8, the essential function and its use are the same. You wrap your text in your module code in T with the same optional placeholder and options arrays. As noted in the placeholder section, exclamation variable is deprecated and replaced with colon variable, but in Drupal 8, we also now have the Twig templating engine. This means new ways to format our text for translation in templates. The simplest way is to pipe your text through |t. Here's an example from the contrib module, where we have a handful of strings and then just pipe T. This is exactly the same as t(string). The text is just piped into the translation function, just as it will be passed through t(). You can also use pipe trans interchangeably with pipe T, and you can use a trans block in Twig to translate a larger chunk of text, or to use placeholders.
These blocks can also handle logic for plurals, which is really cool. Here's an example from Drupal 8 Core where we have "if displays", "trans". So, we're translating all of this and then we have "Display" for the singular, "plural displays", "Displays", and then "endtrans". It's that easy to translate and handle plurals in your theming in Twig and Drupal 8. Wrapping up, we've gone over a lot of what not to do, I know, but you're going to run into a lot of creatively incorrect code when it comes to translation. I think now you're going to know it when you see it, simple is best. Remember that this function exists to give translators a list of strings to translate, and now you'll be in the right frame of mind when assembling these strings to keep them flexible and translatable. There's also more to dig into with Twig and translations and logic in Drupal 8 if you want to look into that on drupal.org. Next up, we're going to talk a little bit about object-oriented coding and Drupal 8 standards.
Object-oriented programming, or OOP, is a way of programming that is based on the concept of objects which represent data in a program. Objects have properties which hold data and methods which execute functions. After an object is created or instantiated, it can be used over and over again. OOP allows for a lot of reuse and convenience that procedural programming does not. If you're not yet familiar with Drupal 8 and OOP, you may want to brush up because we're going over best practices and formatting here and not concepts. The OOP examples project on drupal.org may be very helpful, and a note, all of these examples are from Drupal 8, and while you can certainly use object-oriented code in Drupal 7, and many people have, it's now mandatory, so get used to it.
Also, I started gathering some of these examples several months ago, and it's possible that they could be slightly out of date. If you happen to maintain any of these modules, and this is your code, and it's out of date, I apologize. Let's start with classes. Declaring classes, there should be only one class, interface or trait per file, and the file should be named after the class or interface. An example from the ctools contrib module. This file is called EntityFormWizardBase.php. The class is called EntityFormWizardBase. This is really straightforward and probably something you've already been doing if you've ever created a class file. I'm sorry, some of these are a little cut off even though officially they're the right size.
Class naming is important for autoloading. Autoloading allows for classes to be loaded on-demand instead of a long list of require statements. From drupal.org we have, "In Drupal 8 classes will be autoloaded based on the PSR-4 namespacing convention. In core, the PSR-4 'tree' starts under core/lib/. In modules, including contrib, custom, and those in core, the PSR-4 'tree' starts under modulename/src. This is just explaining how the structure of your module should go.
From the PSR-4 autoloader documentation, which is actually really brief and worth looking over, "This PSR describes the specification for autoloading classes from file paths. This PSR also describes where to place files that will be auto-loaded according to the specification. All that's going on here is that PSR-4 is telling you how to create your file paths so that classes can be autoloaded. Most of what matters here is how you name and arrange your files and directories. The rest is already happening for you behind the scenes. A note on the file docblock in Drupal 8. The current Drupal standards state, "The @file doc block must be present for all PHP files with one exception, files that contain a namespaced class, interface or trait, whose file name is in the class with a PHP extension, and whose file path is closely related to the namespace under PSR-4 or a similar standard should not have a @file documentation block."
Like the example that we just looked at where the name of the file and the class were exactly the same, as long as the namespace and the path were also the same, it didn't need a file documentation block because we already knew what the name of the file was. This was adopted after most of the Drupal 8 code was written, another reason to keep your code standards handy. This is why you are still seeing @file blocks in PHP files that don't actually require them. I haven't edited any of the snippets that I'm quoting here. Be aware that this is a new standard, and you will probably be seeing this adopted in module code.
Namespaces are a way of organizing codebases. First, let's look at an example of a namespace from the Metatag contrib module. The file doc block here, which, again, probably would not be needed, but is helpful for learning, tells us that this file contains a class called "Drupal\metatag\Command\GenerateGroupCommand", and then we see the namespace declaration for "Drupal\metatag\Command" and the documentation and declaration for the class GenerateGroupCommand. You can see how all of these names trickle down. If we look at the directory structure, and this was a screenshot of my directory, you can see under metatag there's Command, GenerateGroupCommand. Remember that the PSR-4 directory tree starts under src/. src is not included in the namespace itself. Whenever you make a Drupal module in Drupal 8, you follow the directory structure <modulename>/src/<namespace>
. Here we have metatag, src, Command. In the example that we were just looking at, there was a whole list of classes that were going to be used in this file.
Any class or interface with a backslash in it must be declared like this at the top of the file. There's a whole handful of them. These are called "fully-qualified namespaces" and they can now be referred to by just the last part of the namespace. The fully-qualified namespace may no longer be used inside the code. In the class GenerateGroupCommand, the use statements here you can see at the bottom-- You can't really see at the bottom because it got cut off, but those all say 'use' in front of them. They refer to the same namespaces used at the top of the file but there were not using the entire name. No backslashes. Also, there's only one class per use statement. You say use this class, use this class. There's not a whole list of them. If you have two classes with the same name, that's called a collision and we fix it by aliasing the namespace.
You use the next higher portion of the namespace to create the alias. Here we've got two extension manager interface classes being called and they're aliased to ReaderManagerInterface and WriterManagerInterface, since they're Zend\Feed\ Reader and Zend\Feed\Writer. Actually, that doesn't happen a lot, FYI. It took a long time to find an example, but if it happens, that's how you handle it. An exception to use statements is if you are trying to use a global class. In that case you don't need to use anything at all. Here's an example from the Devel contrib module. In this function formatTime, we can see throw new invalid argument exception. That's not declared anywhere in the file because it's a global class. It's a part of Drupal core. In fact, it's a part of Symfony, on which Drupal 8 is built.
If you're curious, it's Symfony component dependency injection exception invalid argument exception. Now you know. Remember that, there's quiz. Indenting and whitespace. Formatting basics do not change in object-oriented programming, but there are some specific conventions. There should be an empty line between the start of a class or interface definition and the property or method definition. Here's a token contrib module example. The code declares an interface, TokenEntityMapperInterface and then leaves a blank line before the function declaration. So, blank line and then again, the beginning is cut off but that's the function doc block and then the beginning of the function declaration. Indenting and whitespace. There should be an empty line between a property definition and a method definition.
Again, from the token contrib module, we've got this code declaring a property, entityMappings, and then a blank line before the function definition for the constructor here. Some of those are hard to remember, so I have those bookmarked.
A lot of just differences in adding blank lines where they were not in Drupal 7. Indenting and white space. There should also be a blank line between the end of a method definition and the end of a class definition. Also very new, so there's a blank line between your ending curly braces. Again, in the token module, you can see that there's a blank line, and this is at the end of the file, there's a blank line between this very last function and then the last curly brace.
There's also a few naming conventions that are different. When you're declaring a class or interface, you always use UpperCamel but if you're declaring a method or a class property, use LowerCamel. Class name should never include "Drupal" or "class". Interfaces should end with "Interface", test classes should end with "Test". There's a whole bunch more detailed conventions at drupal.org. Here's a nice example from the Google Analytics module. There's a bunch of properly named classes here, including google_analytics\Tests\GoolgleAnalyticsBasicTest. We've got an account interface, a web or they're using an interface.
We've got a class GoolgleAnalyticsBasicTest extends webTestBase. They're using UpperCamel for the classes. Interfaces, these can be a little tricky to understand, but for flexibility reasons, we strongly encourage that you create interface definitions and implement them in separate classes. If there was the remote possibility of a class being swapped out for another implementation at some point in the future, split the method definitions off into a formal Interface.
A class that is intended to be extended must always provide an Interface that other classes can implement rather than forcing them to extend the base class. Here's an example where we have an interface and then a class implementation. Excuse me. This is from the ctools module. We've got ConstraintConditionInterface declared. Then, it's got two functions, applyConstraints and removeConstraints. We're not doing anything with this, we're just declaring all of it. Visibility. All methods and properties of classes must have their visibility declared. It can be public, protected or private, but public properties are strongly discouraged. An example from the metatag contrib module, this code declares a protected property, token, and a public function, construct. I think you can see all the way at the bottom.
Type Hinting is optional but recommended. It can be a great debugging tool. If an object of the incorrect type is passed, an error will be thrown. If a method's parameters expect a certain interface, specify it. Do not specify a class as a type, only an interface. This ensures that you are checking for a type but it keeps your code fluid by not adhering rigidly to a class. Interfaces really help us to put some constraints on things, but not to make it so constrained that we can't continue to use our code and reuse it for other purposes. Keeps code reusable by checking for the interface and not the class, allowing classes to differ. Here's an example from the Pathauto contrib module. In this code, we have the property definitions function which has the parameter, "field definition", which is sort of running off the edge of the screen there. It has the type hint, FieldStorageDefinitionInterface. We're using an interface here instead of a class.
There could be a variety of different implementations of that interface and they would all pass here. Chaining is my favorite thing, and it allows you to immediately call a function on a returned object. You're probably really familiar with this with database objects. We've got this example from the Devel Node Access contrib module, where the results of db_query are chained in to fetchField. Without chaining, you'd have to set the results of that query into an object like $result, and then you could use the fetchField() function in another statement. To allow for chaining whenever possible, methods that don't return a specific value should return $this. Especially in methods that set a state or property on an object, returning the object itself is much more useful than returning a boolean or NULL because if you're trying to chain things, you're just going to cut it off and you're not going to get any valuable information out of that.
Constructors and Instantiation. Drupal's coding standards discourage directly creating classes. Instead, it is ideal to create a function to instantiate the object and return it. Two reasons are given for this. One, the function can be written to be re- used to return different objects with the same interface as needed, and, because you cannot chain constructors in PHP, but you can chain the returned object from a function, and as we said before, chaining is my favorite thing.
Here's an example from the Metatag contrib module, the function that returns a new instance of a metatag generator. The createGenerator function is declared in an interface that is implemented by this class. This wraps up the Object Oriented Programming section. If you need more resources, when I post this, you can check out these links. They've been really helpful to our team, along with, as I mentioned before, the OOP examples module, which is not really a module in and of itself, but it's a bunch of code examples. Next up, Twig in Drupal 8. Twig files should include a docblock like any other Drupal file. Sections such as @see, @ingroup, et cetera still apply as they did before Twig, so use as appropriate. One note on @ingroup themeable from Drupal.org, Twig template docblocks should only include @ingroup themeable if the template is providing the default themeable output. For themes overriding the default output, the @ingroup themeable line should not be included. It only gets @ingroup themeable tag if this is the default output. Comments are wrapped in the Twig comment indicator, which is the pound sign inside of curly braces. Short and long comments use the same indicator. Long comments should be wrapped so that they don't exceed 80 characters. Comments that span several lines have the indicators on separate lines.
Here's a quick example of a short comment from Drupal 8 core in the Field UI module, very straightforward, and an example of a long comment. This is in the Book module in Drupal 8 core. As you can see, the common indicators are on separate lines, and the text is indented. Variables should be referred to only by name in the docblock with no prefix. If you add a variable named foo, you wouldn't use the dollar sign or curly braces around it. The type shouldn't be included in the docblock. It doesn't have anything to do with theming. Variables referenced inline in a docblock, meaning inside of a sentence, should be wrapped in single quotes.
Here's an example from the token module which heartbreakingly is also cut off. In this code, if it's not completely cut off you can see that the docblock lists the available variables. It says URL, text, attributes, and link. They're listed without a prefix or a type. This is the default template, so it uses ingroup themeable. You'll also notice that there is no period at the end of any line that starts with a tag. Variables referenced inline in a docblock are wrapped in single quotes. This quote is from comment.html.twig which is a really, really, really long doc block but in this snippet, we can see that these variables are properly referenced, and wrapped in single quotes. If you look in the third line, we have the desired parameters on the 'comment.created' variables, so that's how you would want to reference a variable inside of a sentence in a docblock.
Expressions. This is so fun. Twig makes it really easy to check if a variable is available before using it. Just use if. How sensible. Here's an example from Drupal 8 core. In this code, we can see that the code checks for the availability of 'label' with 'if label' before printing it. This is a lot simpler than previous methods of checking, which required much more complex structures and much more code. Expressions. Loops are also much simpler than Twig. You can easily use 'for' loops in Twig. Here's an example from the Devel contrib module. This code loops through and prints out each item in collector.links. You can also do 'for each' loops, which I don't have an example of here, but I do in my blog posts online. It's incredible how much easier this stuff is to do in Twig.
Another simple expression that can be done in Twig is variable assignment. If a variable is needed only in a template, you can just declare it directly as you would anywhere else. Literally, just say my variable equals, my variable value. Done. HTML attributes in Drupal 8 are drillable. They can be printed all at once, or one at a time using dot notation. If you don't print them all. They should all be included at the end, so that any other attributes added by other modules will be included. If you're not familiar with HTML attributes in Drupal, check it out in the Drupal.org documentation, and the Drupal Twig documentation on HTML attributes.
Here's an example where, in this code, the full attributes are printed for the <a>
and the <span>
tag. They've got 'a' attributes, name, extra and 'span' attributes, name, extra. You can also add or remove a class in Twig. Here's an example from the Views module. In this code, an array of classes is created and then added to the HTML attributes. There's also a thing that could be much more complicated, and Twig makes it a whole lot easier.
Whitespace control. This is another thing in Twig is I think is super cool. The spaceless tag removes whitespace between HTML tags. Wrap your code in this tag whenever you want to remove whitespace. Here, this entire chunk of code is wrapped in the spaceless tags which are cut off here. The whitespace control character, which is just a hyphen, removes whitespace at the tag level. Here's an example in Drupal 8 core, and it's just added to the delimiter. We can see that the whitespace control character is added to the delimiters, indicating the whitespace should be removed on both sides, because you can also use it to remove the space from just one side if the whitespace character is only added to the delimiter on one side or the other.
A caveat regarding new lines at the end of files. Drupal coding standards require that all files have a new line at the end of the file. If you have PHP codesniffer or any other test set up for Drupal, it's going to require this. In Twig, you may not want this in your template output. Until a better community-wide solution is reached, you may want to alter your tests to get them to pass or there are some Twig template tags you can add to the end of your file. There's an ongoing issue here. For more clarification on how to do this-- It's not that complex but it is a little annoying.
A filter in Twig uses the pipe character. We talked about this with the t() function to translate, but there are other filters that you can use as well. Here's an example of a Twig filter, 'join', from Drupal 8 core. In this code, the 'join' filter is applied to collector.roles, which concatenates the items, separates them with a comma and a space, as indicated. That's a pretty cool thing. Very simple to use. There's some nitty gritty details, I'm just going to go over real quick for syntax. I don't have examples for all of them since I don't have another hour. I want a few more minutes. There are examples online. Again, you can see them when I post the slides.
These standards are taken from the Twig documentation at Sensiolabs. org. Put one and only one space after the start of any of the Twig delimiters, and before the end of a delimiter. When using the whitespace control character, you never want to put a space between that character and the delimiter. Put one and only one space before and after all of the following operators. If comparison operators, math operators, logic operators, not, and, or, a tilde, is, in, and the ternary operator. Put one and only one space after the colon sign and hashes, a comma, and arrays and hashes. Do not put any spaces after an opening parenthesis, or before a closing parenthesis in expressions.
Do not put any spaces before and after the string delimiters, and do not put any spaces before or after these following operators, the pipe, dot, two dots, and the square brackets. Do not put any spaces before and after the parentheses used for filters and function calls. Do not put any spaces before and after the opening and closing of arrays and hashes. Use lowercase and underscored variable names and indent your code using two spaces as throughout Drupal. To wrap up the presentation, remember to keep your coding standards handy. Refer to them often, they change. Check out the coding standards issue queue and keep your code clean. Thank you.
[applause]
Alanna: If anyone has any questions, we're out of time but you can find me. I'll be either in the back of the room or just outside.
Speaker: [unintelligible 00:59:32].
Speaker: What was that?
Speaker: [unintelligible 00:59:36].
Alanna: I don't have them up yet. I'm going to put them on the page for the session.
Speaker: I'm going to need it [unintelligible 00:59:45] .
Speaker: I'll put it up in a couple of minutes, as soon as I can. Thank you. Hi. Oh, thank you for coming.
Speaker: That was so great. [unintelligible 00:59:57] a lot of it, but when I saw [unintelligible 01:00:00]. This is something incredibly satisfying about having everything organized.
Alanna: That's how I feel.
Speaker: [unintelligible 01:00:13] . I'm a divert tabber, but I would do it in three spaces.
Alanna: You can't be a tabber.
Speaker: Because it's the rules.
Alanna: I know I'm all about diversity and inclusion, but not if you're a tabber. Sorry, we can't have you here.
Speaker: I was banging my head against the wall for like two hours and I found that it's because I had to tab. [unintelligible 01:00:31] exhausting.
Alanna: Oh, my goodness.
[01:00:40] [END OF AUDIO]