 Gweithdoedd yn fwy, mae'r ffordd. Mae'n gweithio Gareth Ellys, ac mae'n gweithio'r ysgolwyr yn y Gweithgol Gweithgol Llywodraeth ac yn ymgyrch yn cyd-i'r cymdeithasol. Mae'n gweithio'r gweithgol Llywodraeth yn y Gweithgol Oxfordd Gweithgol. Felly, mae'n gweithio'r gweithgol Oxfordd yn y gweithgol Llywodraeth, mae'n gweithio'n gweithgol. A rhoi'n gweithio'r gweithio'r gweithgol fel teulu ar gyfer Coleisio. Mae wylawn i'r gweithgol ac rhaid chi'n gweithio'r gweithgol fel teulu ar geithasol. Felly, mae'n gweithio'r gweithgol fel teulu ar gyfer gweithgol Llywodraeth. Rwy'n gweithio'r gweithgol a'r hynny nido, gallwn ei gweithgol lleiaf ar y cyrd困au hynny, ac yn ymgyrch yn ychwanbeth i'w blynyddiol. A i'r ddweithgol cyfans o'i gwirionedd ac o'r cyfans o'r gweithgol. are five guidelines for object-oriented design. They were first formulated in the late 90s under the name of the first five principles by a developer called Robert Martin, who some of you may know as Uncle Bob. And then in the early 2000s another developer called Michael Feathers came up with this acronym SOLID that we use today to describe these principles together. The idea is that when you apply all of the SOLID principles together, to your code, it will result in code which is easier to maintain, easier to read, easier to extend, and easier to reason about. So what does reason about mean? Because I think when I first heard that phrase I just assumed it meant understand, but I think it's subtly different from understanding. Code that we can understand is code that we can look at. We might have to ask a colleague to help explain what it does. We might have to go and read documentation. And after those steps we might arrive at understanding the code. Whereas code that's easy to reason about is code that you can just look at, you can just understand it because it's self-explanatory, it's clear. And I think that's a really worthwhile goal of any code that we write. SOLID principles are about object-oriented programming in general. They're not about PHP. We need to bear this in mind when we interpret the wording of these guidelines. When you're trying to apply SOLID to your work, the goal is not adherence to the goals themselves. There's no gold star. This achievement doesn't exist. As software developers we should all know that every decision we make has a trade-off or trade-offs plural. And we need to be aware of what those trade-offs are at every point. And you may come across occasions when the trade-off for not applying SOLID to your work is worth it. And it's up to us to know those trade-offs and to be able to make informed decisions for our use cases. You will find that writing SOLID code means you are writing more code full stop. And I think this is okay because as developers we spend way more time reading code than we do writing. I think we can all relate to the idea of working on a problem, writing code and being right in the headspace of that problem, right in that context, having all the information around us and we write code. Then we come back to it a week later and we look at it and we go, I'm not really sure what I was doing there. That doesn't make sense to me. And that's because we've optimised for writing. We've optimised for saving a few keystrokes instead of making sure that our code is easy to read and easy to reason about. So on with the five principles. And the first of them, as you may guess, is the S. And that is the Single Responsibility Principle, or SRP. SRP states that a class should have a single well-defined purpose. Or, more specifically, it has one reason to change. So what's a reason to change? Well, in my opinion, a reason to change is something which can possibly drive a change somewhere in your code. So, for example, if you have a class which deals with talking to the database, the only reason for that class to change would be if you change something about the way you're talking to the database. So you're going to change a query, for example. Classes shouldn't get ideas above their station. A good rule of thumb is that you could try to define the purpose of your class in a single sentence without using the word and or or. And if you can't do that, your class might be doing too much. In my experience, and certainly in experience of applications that I have written in the past, controllers in MVC applications tend to be quite big violators of this principle. So here's some code I've completely made up for the purposes of this talk, and obviously it will seem familiar to some of you who have worked on applications of this type. A fairly common function for a lot of applications is to have a method for users to register. So here we have a user registration controller. It has a method called register, and we're passing in a request and a response object. So what's the controller's reason to change? Controller's concern should only really be with control flow. It's called a controller for a reason. The job of the controller is to take the request and return a response, talking to the domain or the model or whatever you want to call it in between. But it's very easy to stuff business logic into our controllers and make them bloated. So in this example, we've got a user that we're creating from request data. We're making an entity. We're then going to save it in the database in the controller. So we've got some SQL in the middle of our controller. We're then going to send a welcome email to the user saying, hey, thanks for registering. And then we're going to return a response. So this controller has got several reasons to change right here because it's dealing with logic to do with the database, it's dealing with logic to do with sending emails, and it's got to do its job as a controller as well. So the first step to refactoring this would be to move that logic into a separate class. So you could call this a service class if you like. I've named it user registration here. And what we're going to use this for is that instead of stuffing that logic into the controller, the controller will talk to this service class. So the controller knows I am a controller method that is used when a user registers. I'm going to take the user, I'm going to pass it to the service class and then I don't care what happens after that. It's none of my business. I'm a controller. So the service class will then deal with what needs to happen next. So here we've got the dependencies in the constructor for the database dependency and the email dependency. And we're assigning them to class properties. And then in the register method of the service class, which is the method that the controller will call, we can call those same methods from the controller. Now we can still do a little bit to improve this because all we've done at the minute is copy and paste code from one class to another. This class has more than one reason to change because it's got logic to do with saving in the database, sending emails. So we could refact that further and we could move the logic to do with saving to the database to a separate class called user registration storage, for example. And the email logic into another class called user registration welcome email. And once again, we assign those to class properties and then we can call those in the register method. So now this class has fewer responsibilities. We could improve this further using, for example, the observer pattern and that's something I'm going to briefly touch upon in the next section. Because the O in solid stands for the open and closed principle. Open and closed states that software entities, which we can think of as classes, should be open for extension but closed for modification. That is, we need to be able to change the behaviour of a class without editing its source code or you should need to cut your chest open just to put on a different code. Closed for modification does not mean we should never, ever change source code. I think our jobs would be pretty difficult if that's what it meant. But just as a little example, how many people here have had a class which is doing its job, working, hasn't got any bugs that you know of and you have to change the functionality of it in some way. You have to change its behaviour, so you make a change and then you introduce a bug. Put your hand up. Yeah, quite a lot of people and some liars. So changing code is a really common entry point for defects. So if we can design up front for extension, design up front to anticipate ways in which we might need to use our code in the future, we can hopefully avoid having to make source code changes and avoid the likelihood of introducing bugs. On the other hand, there's always the chance that you ain't going to need it. So the trade-off here is how do we work out in what ways our classes might need to be used in the future and that takes experience, it takes practice and you just need to kind of get an inkling and a feeling for in what ways you might need to change things going forward. So it's a fine balance between not writing too much code and closing yourself off to future extension. So OpenClose states the classes should be open for extension and what does that mean? When I first read this as a PHP developer, I immediately thought extension, that derives from the word extends, which is a keyword in PHP, it's what we use to create a subclass. So I thought, well, okay, that just means we need to be able to extend classes. It's inheritance, right? Open for extension means inheritance. But solid isn't about PHP. PHP does have the keyword extends. PHP has inheritance. But there are some object-oriented languages that don't. For example, Golang, I believe, doesn't have the classic inheritance model that PHP does. It has alternative ways you can get similar behaviour, the same approach built in. PHP itself has the final keyword, and when you prefix final on to a class definition, nobody can extend that class. You can't subclass it. So the truth is that inheritance is just one tool available to us for ensuring our classes are open to extension. And like all tools, we need to know what strengths and weaknesses they have. Inheritance has its uses. I'm not going to stand here and tell you I don't use it because I do. But you have to know your tools, like I say. But in general, we should try to favour composition over inheritance. Composition is one of the fundamental building blocks of object-oriented design. It's the idea that we can abstract things which vary between components of our applications and compose them into separate units or classes. So I've got an example for you of how composition might be better than inheritance in a certain situation. I've adapted this example from a really great book called Headfirst Design Patterns. I've got a link to it at the end of my slide. If you are not familiar with design patterns and you want to learn more about it, I really, really recommend this book. It's a fantastic read. So let's say we're working on a duck simulator app. Everyone likes ducks, right? So in this simulator app, we're going to have a screen and it's going to have ducks flying around on it and doing things that ducks do, like quacking and swimming and duck things. So if we were to approach this using inheritance, we might do something like this. We might have an abstract class called duck which has methods swim, quack and fly. And in those methods, we're going to have the logic concerned with actually performing those behaviours. So we don't have real code here because it's not a real application, but hopefully you can use your imagination. And then we can add different types of duck to our application by extending the abstract class. So we can have a mallard and an idder and a mandarin and so on. This works great, doesn't it? Because all ducks quack, fly and swim. Then the boss says we need to put a rubber duck into our application. Oh. So we extend our duck abstract class. Ducks, rubber ducks don't swim. They just float, bob on the water. So we have to override the parent method there. They don't quack either. They squeak when you squeeze them. So again, we have to override that parent method. And they don't fly. They don't have anything you could describe as flying unless throwing it across the room could be called flying. And of course they have an additional behaviour because they can help us debug our code. So we're finding ourselves having to override every method from our parent class, which is a bit of a sign that something's wrong with our design. Now, this might not be a big problem in this specific example where we've only got four types, but as we scale our application, we're going to run into problems maintaining this. So let's say we have to also add a wooden toy duck to our application. We'll add similarities with a rubber duck because it doesn't swim. It doesn't quack. It doesn't make any noise at all, in fact. And it doesn't fly. So again, we're overriding methods from the parent class. We're having to duplicate code. We're going to run into problems scaling this because we're going to have behaviour code spread about different classes and it's going to get messy. So if we were to approach this problem using composition, we might do something like this. Let's say we create interfaces called swimming quacking with a method quack and flying with a method fly. We're going to encapsulate the thing which varies between the ducks, which is the way that they perform these behaviours and we're going to compose those behaviours into their own classes. These interfaces will not be implemented by the duck classes. They will be implemented by classes whose job, whose single responsibility is to perform those behaviours. By doing this, we also make our ducks more compliant with single responsibility principle because the duck classes are no longer concerned with how they fly, swim or quack. So we might implement this something like this. We've got the interface quacking, for example. We'll have a quacking duck behaviour which the regular ducks can use. They'll make a normal quack noise. We'll have a mute duck for the wooden duck and we're going to have a squeaking duck for the rubber duck. And then it might look something like this. So we've got the ida duck class whose constructor would look something like this. And if you look in the constructor here, we can see we've got tightpence on those behaviour interfaces, swimming, quacking and flying. So we inject in instances of those behaviours, assign them to class properties and then in the methods, the swim, quack and fly methods on the duck class, we just delegate to those behaviours. So what does this have to do with the open and close principle? Our duck classes are now open to the duck class. So we've got the duck class and we've got the duck class and our duck classes are now open to extension. Because these tightpence in the constructor here are interfaces. So we can pass in alternative behaviours if we want and change the way that this duck behaves. Now ida ducks are a particular favourite of mine because they have a really cool mating noise. It kind of sounds like this. Let's say some users of our application want ida ducks instead of quacking to make the noise that I can't believe I just made on stage in front of a couple hundred people. We might create a class called ool which implements our quacking interface and it would take care of the logic of making that ridiculous sound. And then we can inject that into the ida duck class by passing it in in the constructor. So we've changed its behaviour without having to change its source code. It's open for extension. And that approach is a pattern which is known as the strategy pattern. Very common pattern you'll come across is a powerful way of changing behaviours by passing in different implementations of interfaces. Another way that we can keep our classes open to extension is to use something called the decorator pattern. I like to think of the decorator pattern as a bit like sandwiches. I like sandwiches. The decorator pattern is... I'll rewind slightly. Imagine you're going to a shop like Breton Monge or Tesco or somewhere where they sell sandwiches and you take one out of the fridge and you take it home. That sandwich is closed for modification effectively because if you want to change the sandwich let's say you bought chicken and mayonnaise and you don't like mayonnaise. You've got to scrape the mayonnaise off the bread. It's not going to be very good, is it? Whereas if you go to somewhere like Subway they have this kind of production line process. You go up and you'd say I'd like a wheat bread roll with chicken in it, please. And then they pass it along their production line and at each stage of that production line you can choose whether you want to decorate your sandwich with salad or sauce or cheese or whatever it might be. Subway sandwiches are effectively open for extension. You don't have to go all the way back to the start and start again if you decide that you want mayonnaise in your sandwich. That's the decorator pattern, effectively. I've got a real example for you. But has anyone in here done any implementing of PSR7 middleware in something like Wim or Symphony or something like that? OK, well, if you've done that that's the decorator pattern, effectively. This idea of passing in requests and response objects down a chain, the middleware stack and operating on those objects and when you get to the other end of the chain those objects will have changed. They would have been decorated. So here is a real example adapted from something we did at work. So one of our applications had to integrate with a third party API. And that API uses OAuth2 for its authentication. So every time we hit an API endpoint to request some data or post something or whatever we're doing we have to send a valid OAuth2 token. Tokens from this API were valid for 48 hours. And we were going to be making multiple requests to this API every day. So to us it didn't make sense to just request a new token every time we were going to hit the API. We could store an existing token for 48 hours and reuse it. So we realised we could achieve this using the decorator pattern. So the first key step to a decorator pattern is having some common interface that your classes can implement and that you can chain together. So in this case it was called access token repository and it has a single method called get which returns an access token. So this will represent somewhere where a client code can call this get method and get an access token object back. That's its contract. So we then made a class called API repository and this will be a class that we use to get a new access token from the API. So in the constructor of this class we pass in an instance of a class called provider which came from the PHP league of extraordinary packages OAuth2 client. Really great library if you want to work with OAuth2 by the way. We assign that to a class property. And then in the get method we can just delegate to that provider class and call the get access token because we know that that will give us that access token object. Now if we want to store that token we could of course go back and change this and put storage logic into that code. But for a start that's not making our codes not going to be very flexible. If we ever want to take the storage logic out we've got to change its source code so it would be open for modification. We'd also be adding additional responsibilities. This class is really simple because all it cares about is getting a token from the API. We shouldn't be adding additional responsibilities to it. So according to the decorator approach we make a new implementation of our access token repository interface. So here it's called storage repository and this will represent somewhere where we can get an access token from a store of some kind. Whether that's a database or file system or redis or whatever you like. And in its constructor it has two dependencies. One is token storage which is another interface we created. I don't have it on the slides but hopefully you can imagine what it might look like. And that will just be something that will deal with talking to whatever store you're using. So if you're using a database that would contain PDO and some queries or whatever. And then the other dependency you can see on the second line of the constructor signature is another instance of access token repository. And the eagle-eyed of you will notice that access token repository in the constructor signature is the same type as in the implement statement at the top of the class. So what this means is we can ask the storage repository to get as a token from its store. We can check whether it's valid or not. And if it's not valid we can call that other repository instance we passed in as a fallback method. So in our case we're saying check the database for a storage check the database for a token and if we don't have one we're going to call the next decorator which in our case will just be the API repository. And in this case after we've called it we've got the token back the storage dependency can then save the token in our database again so that next time we hit it it will be there and valid and then we can return it. The important point to note is that this instance of access token repository that we pass in could be any implementation we like. So if we want to add additional decorators to our chain we can do that. So here's what the usage of that code would look like we have to instantiate a few different things and wire things together. This is an example of solid meaning we're having to write a bit of code. Solid tends to put the burden on the calling code. This is an example of that because we've got to set up all the dependencies. And then what I think is really nice and really simple is that the client code ok it does have to deal with wiring together these dependencies but then it can just call one method and it gets the token back and it doesn't need to know anything about what's happening underneath that. It doesn't know that there's any decorator pattern involved there. It doesn't know whether it's getting a token from storage or from the API it doesn't care. It doesn't need to know. So if we wanted to add an additional decorator here let's say if we wanted to log every time we were hitting the API to request an access token we could add a new implementation of our repository and sandwich that in between the different layers of our decorator setup. The final pattern I want to briefly mention, I don't have time unfortunately to go into any detail on this is the observer pattern and this is another way you can keep your classes open for extension. If we think back to that controller example where we refactored out into a service class we could make that class more open to extension by using the observer pattern. All I can say is this is a really interesting and useful pattern if you've ever used frameworks, events, subsystems they tend to use a version of the observer pattern and there's some really great articles out there that you can go away and read I would recommend that. There's a good section in that book as well that I mentioned before. So we're on to the L of solid which is the list of substitution principle. Liskof substitution principle was formulated by a woman called Barbara Liskof. She was quite an awesome lady. She was the first woman in the US to get a doctorate in computer science. Her principle states that objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program. Or if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering the desirable properties of that program. Everything seems to come back to ducks. So when I read that wording and I see things like correctness and desirable properties, that says to me it's all about behaviour. So here's an example of a Liskof substitution breach. This comes from Uncle Bob himself. He wrote an article about LSP which is really interesting. I won't pretend to understand it all. It's quite academic in places but it's very interesting read anyway. So let's suppose we have an application which deals with geometric shapes and we have a class called rectangle which is going to represent a rectangle. And a rectangle has a height and a width and we need to be able to set and get those. This isn't a very good class. It's completely mutable and so on but let's just ignore that for this example. We then have a function called transform which again I'm not really sure why it would exist in real world but here we go just serves the purpose of this example and when you pass in a rectangle to this function it's going to change the height of the rectangle to 10. So we'd instantiate a rectangle, give it a height and width and then we'd call our transform method transform function. The idea would be that if we check the height and the width of the rectangle after we've called transform the height will have changed but the width will have stayed the same. That is the desirable property of the rectangle class. Transform function when it calls set height it doesn't expect anything to happen to the width. So let's say in our application as well as a rectangle we also need to have a square. Now squares in mathematics and geometry are inarguably a type of rectangle. So we should be able to just subclass a rectangle right? Create a square like that. So let's do that, let's see what that looks like. So here's our square class now squares the fundamental property of a square is that it's height and width are always the same. So we can get rid of the height and width properties from rectangle and replace it with a single height and width property. We then have to override the setters to deal with the different property. Once again, as with the duck example before, the fact that we're overriding all of those methods is a sign that there's something wrong with our design. But here's the problem. When we pass in a square to the transform function which PHP will allow us to do because our rectangle type in here will allow us to pass in a subtype like square and it's called set height. The height will have changed but so will the width. So from the point of view of the consumer of the rectangle type which is our transform function this is performing undesirable things unexpected things. Who's heard of a concept called design by contract? A few of you? Really interesting topic. It's worthy of many talks by itself but there's a good Wikipedia article where I'm going to get an overview on it. There's also a couple of RFCs in discussion at the minute I think for adding native design by contract features to PHP which could be interesting. And in his article anyway, in his article Uncle Bob talks about strong correlation between LSP between Lisk of Substitution Principle and design by contract. One of the things that design by contract talks about is the idea of pre-conditions and post-conditions. So if we could have a look at what that might look like for our rectangle class the pre-condition of our set height method could be for example there is a rectangle and it has a given height and width. The post-condition of the set height method what we would expect to be the case after we've called it would be the height of the rectangle has changed but the width has not. So this is a useful approach for assessing the substitutability of subclasses. We can come up with these pre and post conditions and we can write tests to assert whether we are meeting those conditions. In general I think the square and rectangle example is another illustration of the problems that can be introduced by inheritance. I would go back to the point I made before about favouring composition and I think LSP pushes us towards composition away from inheritance. The I in solid stands for interface segregation principle and interface segregation principle or ISP states that many client specific interfaces are better than one general purpose interface. No client should be forced to depend on methods it doesn't use. You can think of it as breaking down larger interfaces into smaller ones smaller and more specific ones. Uncle Bob first formulated ISP when he was contracting at Xerox in the late 90s Xerox obviously make these big corporate printers and photocopiers all in one type things and the software that was running on these had this concept of jobs so you had a print job and a copy job, a fax job etc and apparently the software had one big interface called job which had print method copy method that sort of thing it was big and every time they wanted to make changes to their software it was getting harder and harder because they had this tight dependency on this type which wasn't specific enough for their use case. Before I go into an example I'd also like to mention another talk specifically about ISP obviously in a talk about all of the solid principles I can only talk for so long about each individual one there's a guy called Dan Akroyd who did a really good talk about this at PHP Northwest last year video is not online yet but the slides are so I've got a link to those and obviously he goes into a bit more depth than I can about this in this time so here's an example of ISP at work literally at work this is adapted from something real we did so at the end of last year we were working on an application which had pages which had lists of things on it so we had things like lists of users lists of clients, lists of projects that kind of thing and some of these pages were going to be loads and loads of items hundreds of items so users needed a way to filter those lists and find what was actually interesting to them what they needed to use so we had to include various search options at the top of each page so we had types like drop down so if we wanted to search for projects for a certain client we'd have a select box with the client names in them free text so if we wanted to search by project name we'd have a free text field and date ranges so if they wanted to find projects created within a certain date range there'd be a couple of date pickers that they could use so we decided the way that we could implement this would be to use our friend the decorator pattern and the composite pattern and we created this interface called filter and the idea would be that each of those types of filter date range free text drop down etc would have its own implementation of this interface and we'd additionally have a collection class which would bundle all of the filters together so that they could be used together so this interface has a method called build query from which has two two dependencies one which is a request which is cake PHP's request object and the idea would be that when a user filled in the form it would submit it and the data they had posted would be populated in the query string and we could grab their search data from the query string so request the request object in cake PHP cake PHP has a query method so we can just grab what's coming from the query string using that query method the other dependency is slightly confusingly called query this comes from cake PHP's ORM so this is a database query and the idea would be that for each filter we can look at what the user has actually searched for and decide whether we need to adjust our database query accordingly so here's what the filter collection would look like we have a method add where we can add filters in and then in the build query from implementation we can loop over each of those filters that we've added we can decorate the query object by parsing it in along with the request each filter will operate it on in turn and eventually we'll get back a query object which will have all the right where conditions and joins or whatever we need to do the search for what the user has searched for so here's an example of an implementation of an individual filter class this one's the free text so we pass in the table alias and the field name so for example blog posts table and we're searching on the field title we pass in blog posts and title and then in the build query from method first of all we have to check does this field exist in the query string so if request query this field name we're basically saying query string do you have the field that we're interested in which in the example I just gave would be title so we'd be saying does the query string have a key title if so what's the value if it does have it then we'll be able to add an object by changing the where conditions so here we're just adding a like condition for our SQL and then finally we're going to return the query back so the next part of the chain can decorate the query and so on so the usage is really simple really neat this is what it looks like just add our new free text into the collection like that but then we hit a bug after we deployed and the bug was what if the user searches for zero we had a very specific use case where a user was searching for zero and it was valid now because this is our if statement obviously PHP will evaluate a string of zero as false so this conditional check failed and the search wasn't working so essentially what we needed to do is change this so instead of just saying to the query you know is this true, see or false we have to more specifically ask it does this key exist i.e. is it null and if it does exist does it have a value or is it just an empty string so we started changing all of our filter classes to have something to look like this and as I was doing this I just thought to myself that this doesn't feel right this feels pretty grotty and the reason for that is that this is a leaky abstraction our filter classes shouldn't know anything about the way that cake PHP's request class deals with the concept of keys existing in the query string and this all boils down to the fact that our filter class has a dependency on the request object and that is not specific enough a type for our use case so the way that we approached this and fixed it was we created a new interface called search parameters and we changed the signature of the filters to depend on the search parameters interface instead and as you can see the search parameters type, search parameters interface has two methods, has which returns a bool and value which returns the value of the thing in the query string if it's there we can still use cake PHP's request object we can still use the query string instead we can now wrap that up inside this search parameters implementation so here's what that would look like we inject the request object in the constructor our has method wraps up that slightly grotty logic about the implementation details of cake PHP's handling of query strings so now that's hidden away we don't have to worry about it and then we have a value method which just returns from the query method as we were doing before the revised filters look like this this reads so much better so much more nicely than either of the previous two versions which directly depended on the request object it reads like plain English doesn't it this is something we strive for in our work is to try and write code which reads as much like plain English as we can so instead of dealing with the internals of cake PHP it's now just dealing with a nice interface which is very specific to what it needs to do benefits here of adhering to interface segregation our classes are more loosely coupled we've given our filter classes one less reason to change because it's no longer concerned with internal implementation details of the framework and our filter class itself has moved towards being open to extension because it's dependent on a very specific interface so if we want to change the way that we are sending in those search parameters let's say we want to use post instead of a query string or we want to use JSON data on an API call or even if we want to make a command line tool we can just create new implementations of our interface as PHP developers I think it's easy for us to read the definition of the interface segregation principle and think of an interface like this one an interface is a thing in PHP it's an abstract type it's a way of providing a contract for our classes but Ruby for example is an object oriented language fully object oriented everything in the language in Ruby is an object a string is just an object which has methods you can call arrays booleans the same but Ruby being more object oriented than PHP doesn't have interfaces doesn't have this so how would a Ruby developer interpret ISP if they don't have a thing called interfaces and the truth is that all classes have an interface even if they don't explicitly implement one so if we go back to the IDA class from earlier I'm not going to make the noise again don't worry you can see that at the top there there's no implements keyword it's not explicitly implementing an interface but it does have an interface and its interface is just its public methods and their signature which would look something like this so this interface doesn't exist but you can it's kind of an abstract idea it's out there so we can take this idea and it leads really nicely into a point that Dan made in his talk which is the ISP for PHP developers in particular isn't really about interfaces but it's about types and he summed it up really well with a slide that looked a little like that so let's quickly review what types are Wikipedia helpfully tells us that a type is a classification of data which tells the compiler how the programmer intends to use the data so in PHP we've got complex types and we have scalar types now I've slightly adapted that division because I think it fits this example more nicely so I'm going to say PHP has objects and it has everything else so in PHP we have two types of object really we have abstract types and we have concrete types so abstracts would be interfaces abstract classes and concretes are anything that you can put the new keyword in front of and actually instantiate an instance of so we've seen examples of interfaces like filter, abstract classes like duck various concrete classes throughout this talk and free text for example is a class which is of type free text but it's also a subtype of filter so it is a type of filter as well so in the other column in PHP we have scalars and arrays arrays are technically in the complex type category which is why I've had to adapt this slightly and I think there's some others that aren't on the list there that I've missed out but hopefully you get the general point in PHP we have objects which have methods that we can call and then we have other types in other languages like Ruby that I mentioned and Scala for example they are fully object-oriented languages so everything in the language is an object that you can instantiate and then call methods on even strings, bulls, ints, etc so if we were programming in Scala for example and we had a class which was dependent on an array or a class which returned an array as a result of one of its methods it wouldn't be returning an array like we think of it in PHP as this giant bucket that we can throw anything into it would be returning an object of type array with methods so if we go back to the point I made before that interface segregation is about type specificity then we can say well if we're depending on an array an array has an interface because it's a class and all classes have interfaces we need to ask ourselves is the interface of the class array specific enough for our use case that's what ISP is all about so we don't have that luxury in PHP we don't have these super amazing objects for everything but we can still apply that logic and arrays as I said they're just this big bucket in PHP they're this Swiss Army knife of programming that we can use and abuse to do whatever we want and I would contend that maybe we shouldn't do that sometimes so it's quite a common pattern to see arrays used for holding configuration values or options so this is adapted from another real example we did at work and we had a date picker method on our form helper and the job of this method would be to render a date picker on a form pretty straightforward functionality pretty common functionality and we had two arguments to the method one which was called field name which would just be the field name in the HTML and then options which would allow us to configure on the fly things like the CSS classes or whether it's a required field or not, change the text label that sort of thing the problem with doing it like this is that in the class itself in the method body we've got to have code like this to check each option we've got to check whether the key exists in the array or not and then we've got to check the actual value of it it doesn't read very nicely it's pretty boring to type it's also pretty prone to errors particularly from the point of view of the calling code so here in this example in the calling code I've deliberately misspelled is required field when I run that code I'm not going to get any error message so the only time I'm going to realise it's not working is when I notice that the code's not doing what I want and then I've got to start debugging and trying to work out why and trying to spot this kind of thing is a massive pain as I'm sure you are aware we've also got no auto completion support I'm a pretty lazy coder I really lean on my IDE and it's auto completion support so I always want to have it wherever I can so instead of using an array we could use an object we could use a class so we made a class called date picker options and then we can define each of the options we want in that class so for example is required field which will default to false and then we can turn that to true and false using set required field and we have a getter method called is required field which just returns the value of it so by doing it like this we add some constraints to our options as well because now this can only be a boolean whereas before someone if they wanted to could assign anything they like into that array but now it has to be a boolean because we've got type in support likewise for the label which is a string we can add additional validation to it because we've got a setter method so if for some bizarre reason we wanted to make sure that our label was no longer than 25 characters we can do that and we get auto complete support array so this is what the revised version would look like we pass in a date picker options instance instead of the array the method body now looks much nicer it's easier to read easier to reason about you can look at that line if options is required field and you know exactly what it means you don't have to try and pass array type stuff to work it out and our calling code looks nicer as well declarative it's easier to understand it's expressive code we can apply that logic to other primitive types as well this isn't technically connected to ISP but I think it's quite a nice little tip it's quite common to see functions or method signatures which have loss of booleans for enabling or disabling certain things this is adapted from something I've seen in the wild so let's say this function is effectively a factory for an instance of connection and you're going to call it and it will give you a connection the problem here is that when we call the code we've then got this horrendous line of booleans when you go and look at that code in a week after you've forgotten what each of those mean you're going to look at that and go of course right it's starting to connect with a true false true it's not so bad here because we've got the function definition right above the function call but in reality you're not going to have that so it's really hard to reason about this code so sometimes you might be working with a third party library which has a method function signature like that and there's nothing you can actually do to improve it but what you can do is you can wrap around it you can adapt so you could approach this problem using the builder pattern so if we created a class called connection factory and we pass in whatever we need as dependencies and then we have methods called enable persist and disable persist and those are just going to toggle the persist value between true and false their set is effectively but we haven't used the word set in the method name because it reads nicer and each of those methods you'll notice will return an instance of the same class because we can then use fluent method chaining for making nice readable code now there are other options in that function signature that we had on the previous screen we've got persist enable awesomeness disable flux capacitor I haven't been able to fit them all on this screen but you have to imagine that they're there as well they've got a property and they've got enable and disable methods as well then the most important part of this class is this function at the bottom, the get connection so that's the part that wraps that original connect with those nasty booleans which are so hard to read so we can wrap that function call in this method and we're hiding the implementation detail of those booleans inside this class so now our calling class instead of calling the function can create a connection factory use its fluent interface call the methods and set whatever things we want to do and then call get connection this is many many times nicer to read and easier to reason about than the function call but yeah, we had to write more code which as I said is something you find you have to do more of when you're writing better code so, the final solid principle and the D is dependency inversion dependency inversion principle states that we should depend on abstractions not concretions who has heard of dependency injection? quite a lot of hands dependency inversion or the dependency inversion principle is what we're trying to address when we use dependency injection so when we say a class depends on abstractions not concretions we're talking about a class's dependencies the things it needs to get its job done we've seen several examples of dependencies throughout this talk and here's one of them so here's the IDER class going back to it again and this time I have adapted the constructor and I have removed the type hints and the arguments from its signature so instead of passing in its dependencies we're going to instantiate them in place so we're assigning our behaviour classes swimming duck, quacking duck and flying duck two properties by using new so this class is now dependent on concretions because they are concreted in place if we want to change the behaviours we have to change the source code which as we've seen in OpenClose is something we want to try and avoid so we can adapt it and change it back to something like this where in the constructor we're passing in arguments we've got type hints and then we're assigning to properties inside the constructor body but what I want to note here is that in this example we've got swimming duck, quacking duck and flying duck as the type hints those are not the interfaces we created earlier those are the concrete classes those are things which can be instantiated with new indeed we saw that on the previous slide but this class is now dependent on abstractions even though it's dependent on concrete classes because from the point of view of this class it doesn't know what swimming duck, quacking duck and flying duck are it doesn't know that they're concrete classes or whether they're interfaces it just knows that they are types with methods that it can depend on so if we want to change swimming duck into an interface for example at a later time we can do that and we don't have to change this class so dependency injection and managing dependency inversion principle is not necessarily about using interfaces like I said we could change those concrete classes to be abstract classes or interfaces if we wanted to in the future but the point is the choice is now ours it's not hardwired into that other class because it's dependent on abstract concepts I think we've seen plenty of good examples in this talk of the value encoding to interfaces but DI does not mean you always have to do it as always evaluate the pros and cons of each approach and make the appropriate choice for your use case dependency inversion is not necessarily about objects either go back to my examples of Ruby and Scala as languages which are fully object oriented you could have a class which depends on strings or booleans or whatever and in those languages they're still depending on objects just in PHP they're not, they're not objects they're scalers so let's have an example we have a class called MySQL connection factory and in the constructor we have the username and password for connecting to the database these are concrete strings this class is dependent on concretions because we can't change those strings without modifying the class it's in breach of OCP and it's in breach of dependency inversion a change of credentials requires a change in the source code so just like using dependency injection for objects we can inject strings in from the outside as well and here's what this would look like we just changed the constructor to pass in the values in the outside and now it's up to us where we store those values we can put them wherever we want we can put them in a settings file we can put them in environment variables it's up to us I said before that solid tends to put more burden onto the calling code and I think this is a good DI is a good example of that the bad version where we're not using dependency injection and we're dependent on concretions it's two words and a semicolon but with dependency injection we're having to do more we're having to wire stuff together we're having to instantiate things and hook everything up this is where a dependency injection container can help you so there's several examples of DI containers in PHP they include Pimple, Orin PHP, DI Zen service manager loads of others if you want to know more about those I would recommend going and reading their documentation they're very powerful tools DI containers the trade off of using them in my experience is that it can make your code flow a little harder to follow a little harder to reason about because you take the logic of wiring things together and you put it somewhere else that you've then got to go and look if you want to kind of work out and debug what's going on it's I think simplification through obfuscation and it's a little bit like tidying your house by throwing everything into the cupboard under the stairs you just moved the burden somewhere else so DI containers are a really useful tool for managing dependency injection you don't have to use one to do dependency injection and you don't have to use one to comply with the dependency inversion principle either nearly there you've got one more keynote and then we can go to the party so in summary here are a few points that I ended up writing down after doing the research and writing this talk we spend more time reading our code than writing so we should optimise for reading we should keep our classes small specialised and simple because doing that means our classes are easier to maintain easier to extend, easier to refactor we should favour composition over inheritance but realise that inheritance is a tool that has its uses just like any other we should make our types as specific as we can and use arrays sparingly let's not just throw arrays into everything we need to create dependencies on the outside and pass them in and use interfaces when they're useful finally we need to always know our trade-offs know our tools know the pros and cons and be able to make the right decisions for our use cases thank you very much I've been Gareth Ellis this was my first conference talk so I really appreciate any feedback you can leave on joined in I will tweet the slides in a little while which will have all of these handy links in if you want to go and read any more about it and that is it we've got time for one question anyone's got any about solid or the slides or whatever Wikipedia's got a good start solid and then there's an article about each individual principle and there's loads of blog posts where people have written their interpretations of the rules the head first design patterns talks about each design pattern quite a lot of them it says this will help you meet this guideline and it references solid and stuff so that's another really good resource ok, thank you very much everyone