Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Choosing which classes to use in a php OOP design

I run an arcade site and over the past few years it's had many features added, it's got to the point where procedural programming just looks way too complicated and adding new features or making simple design modifications can be very tricky.

Therefore I've decided to try and recode the site from the ground up with the same features but in an OOP format.

The issue I have is picking the classes, I understand OOP and how it should work but always seem to have trouble getting started. I am unsure whether I should be trying to make functions for classes such as a user class with log in user function or if the user class should just be to add/update/show user details and the log in part would be better in a system class?

At the moment I've started with the following class and functions but do they fit in this category?

<?
class User {

    var $userId,
        $username,
        $userRole,
        $userEmail;

    function isLoggedIn(){

    }

    function login($postusername, $postpassword)
    {

    }

    function increaseLoginCount(){

    }

    function logout(){

    }
}
?>

I could then have something like the following in a page.php .. (connect class not shown)

<?
$db = new Connect;
$db->connect();

$user = new User;

if(!$user->isLoggedIn())
{
    echo "Please Log In.";

    if($_POST['user'])
    {
        $user->login($_POST['username'], $_POST['password']);
    }
}
else
{
    if($_POST['logout'])
    {
        $user->logout();
        exit;   
    }

    echo $user->username." Logged In.<br />";
}
?>

But then the site would have pages to show game categories and I don't know where the displayGames() function would fit as it's not a single game so wouldn't go in the 'Game' class?

I've tried to find 'real world' examples but php code showing me how to make an elephant change colour or dance doesn't really help ...

like image 584
Dan Avatar asked Dec 31 '11 04:12

Dan


People also ask

What is class in OOP in PHP?

A class is a template for objects, and an object is an instance of class.

Should I use OOP in PHP?

OOP is faster and easier to execute. OOP provides a clear structure for the programs. OOP helps to keep the PHP code DRY "Don't Repeat Yourself", and makes the code easier to maintain, modify and debug. OOP makes it possible to create full reusable applications with less code and shorter development time.

Is PHP OOP hard?

Learning PHP OOP from procedural programming is a bit tricky for me. I almost gave up but the demand for reusability kept me going. The concept of class and object seemed difficult for me, but after many attempts and approaches, I was able to get the concepts and the implementation.

What is class in PHP explain it with example?

Class − This is a programmer-defined data type, which includes local functions as well as local data. You can think of a class as a template for making many instances of the same kind (or class) of object. Object − An individual instance of the data structure defined by a class.


2 Answers

Let's start with some textual analysis, highlights by me:

I run an arcade site and over the past few years

Think about how much you are able to cope with and what you are dealing with. Also understand that over the years you have gained specific knowledge about running arcade site. You have gained profession in your area. Never underestimate your own position and assets, it's the base you're operating from and you will introduce changes to. This includes your site's userbase.

it's had many features added, it's got to the point where procedural programming just looks way too complicated and adding new features or making simple design modifications can be very tricky.

If systems grow, they become more and more complicated. That's not specific to procedural programming only, it's a matter of fact. As you're running the site now for many years, you know how things changed, especially in the area how the user interfaces with your site.

Therefore I've decided to try and recode the site from the ground up with the same features but in an OOP format.

It's said that it could be possible to use OOP techniques to make re-useable software, there is (and can not be) any proof of this.

But there are only very few examples in commercial software development where re-writing the whole application from scratch was a success. Very few. The rules of commercial software development might not apply in your specific case, so just saying.

Think twice before you recode the site. Doing a lot of work only to achieve the same is somewhat fruitless and can be disappointing. Instead probably look more specifically which of your current design is introducing the biggest problem you would love to change.

It is possible with PHP to mix procedural and object-oriented style, which can be especially useful when you have legacy code (a common definition of legacy code is code w/o automated tests).

The issue I have is picking the classes,

I try to rephrase that: For what write classes for?

I understand OOP and how it should work but always seem to have trouble getting started.

The beginning is always the hardest step. You're in the position ready to make decisions now, but you can't look into the future. Reduce the risk by eliminating the most risky part. You've probably started to ask a question here to gain some feedback to base your decisions on, but that will probably not reduce the burden and might lead to confusion. However, getting educated is most often a good thing.

I am unsure whether I should be trying to make functions for classes such as a user class with log in user function or if the user class should just be to add/update/show user details and the log in part would be better in a system class?

That highly depends on the nature of your application and the nature of the user. Probably the most of your script only need to know if a user is concrete or anonymous (an anonymous user has not logged in), it's ID, name or nickname.

The application should provide that user to any consuming component, so each command/script does not need to deal with a) obtaining users information and handling users (like logging in), b) verifying if the component is valid for the user (access control). That should be placed somewhere else, e.g. in the application controller­PofEAA.

Each of your scripts/commands that has the application controller object, can ask for the user and just interact with the user.

However this is just technically speaking. Deal with the fact of yourself being unsure, work with that information. Try to better formulate your concrete problem, list pros and cons for a specific way of solving it, get away from concrete code again before starting to code. Then compare pros and cons. Try to make things more simple and less complicated.

Probably write down in simple words what should be happening instead of writing code.

At the moment I've started with the following class and functions but do they fit in this category**?**

Your code is quite bare, so it's hard to say much about it - especially as I don't know what your arcade site is (and what the category is you write about). It's still good for an example probably. What can be seen in your classes is that you tightly integrate everything with each other.

For example you start with the DB. That's common, because the DB is a central component for any application. The application needs the DB to operate on. However you want to keep things loosely coupled so that all your commands can be run with some other DB or a new user object that is connected with some even other DB than the rest of your application's data objects.

 $application->getDB();

As the user is such a central subject in each application, it should have a very simple interface. All the glory details about authentication, retrieving of the users properties etc. should be delegated into another class/component, so that you can change the implementation where users are stored and how they authenticate:

 /**
  * @package command.modules.games
  */
 function ListGamesCommand(Request $request, Response $response)
 {
     $application = $request->getApplication();
     $user = $application->getSession()->getUser();
     $games = $application->getModels()->build('games');
     $games = $games->findByUser($user);
     $response->setProp('games', $games);
 }

As this example shows, you can add the functionality when you need it. E.g. as long as your application does not need to actually log in users, why care about how it's written now?

Create a factory that is producing the user for the application object - whatever it will need now or in the future (see the Two Piles of Objects in The Clean Code Talks — Inheritance, Polymorphism, & Testing). If you then need the authentication, either add it to the session object or the user object interface.

The authentication itself would be implemented in a class of it's own anyway, the Authenticator. So you can review your interfaces later on as well, by moving the invocation of authentication from session to user or whatever. Only a fraction of your commands will need to deal with these specific task and as all new code is under automatic tests as you want to rewrite and benefit from OOP, you have ensured that all the places are covered and properly re-factored.

Same is true for accessing request variables. If you want to make use of the benefits of OOP - which is highly connected with indirection (and each layer of indirection comes with a price) - you should first of all make your base classes operate on specific data and not on any data (like globals and superglobals, I've seen $_POST in your example code).

So enable your new code to operate on a request and deliver a response (Input - Processing - Output):

$request  = new Request($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES, $_ENV);
$response = new Response;

Example taken from the BankAccount - Sample application used for PHPUnit training

Now everything below this can operate on a Request and a Response object - process input and turn it into output. The domain command (your scripts/commands that do the thing) do not need to care any longer about extracting input from the HTTP request like using $_POST or $_GET they can take it directly from the Request - or if you write a class of commands of it's own - this can be even more tailored. And some commands can operate on requests and responses of their own.

The next big topic is the user-interface. You write you want to:

I've decided to try and recode the site from the ground up with the same features but in an OOP format.

I already wrote that such a doing can be fruitless. To have a benefit of OOP code would mean that the next time you change your code, you're still able to re-use components. As software changes constantly, this time now is already the next time. So you want to already re-use existing code. I assume one part of your existing code is the output logic. So the existing output logic needs to interface with the exemplary Request and Response above.

I bet you love websites. You love to make them just work and look great. You've build your site over the years and even not everything is like you wish it to be, you don't want to drop it. So it's crucial for your re-write that you don't destroy everything but you can preserve it's working form from now to the future (See as well Preserve a Working Application; last point of the list).

In webapps the view is such a crucial part. If you loose the view, your site will loose it's identity. If you change too much of it, your site will loose users that are comfortable with using it today.

If you break it,

  1. will you even notice?
  2. can you fix it?

On the other hand you want your application code (the features, the functionality) to not be that tightly bound to it any longer to have a benefit in rewriting your code. As you want to rewrite your application, let's take a look:

     .---------.                            .-------------.
     | website |  ---> [interface in ] ---> | application |
     | user    |  <--- [interface out] <--- |             |
     `---------´                            `-------------´

As this schema shows, to make your application more independent to whatever the interaction looks like (can be a website, a (smartphone) GUI or the ticketing system), the application code should be replace-able. You don't want to code the logic to obtain a users games for example for every type of user-interface in the new application code, but you did in the old application code.

Taking the User object as an example here. How it authenticates and where it is stored shouldn't be something your new application commands code are concerned about. It's just there if the command needs it. Not globally but specifically if the command asks for it.

Where-as the registration and lost password procedures are part of your existing application and continue to exist.

Now you need to bring the old and the new code together.

So you will probably start with an interface for HTTP requests and a HTTP response. The view kicks in with that Interface Out. You assign/pass all needed data for the view via that interface, your application does not know the view any longer. You don't deal with any CSS, Javascript or HTML code within your new application code. That's just the sugar on top for the output. Your application should interface as well via console/telnet in plain text or as a remote XMLRPC service, AJAX endpoint - whatever.

So you can probably just generalize your view code and inject variables to it. To write a view layer could be as simple as including a PHP file. It operates on variables that are available within it's scope. It can make use of "helper" functions (template macros) that are available in it's scope. It can make use of view model objects. It's even possible to write your own language for the view (templating language, a Domain Specific Language (DSL)).

But this is only possible if you create an interface that allows the application code to do so.

So what you now do is to move away the HTTP/HTML/CSS/JS from your application into an adapter of it's own. That adapter is able to formulate the generic command that can be passed to any application via interface in.

The application will only take care to execute the command and deliver it's response via interface out. So you have two domains now: Your application and the website.

You can start to create these two new domains and then offer an interface in and out for your legacy code and one for your new code.

You also have "two" applications next to each other. They are finally bound together (invisible in their own code) with your database which takes care that the data of your site is in order. And that's what the database is for. Separate the data from your code, so you can change your code over time.

Also if you want to re-code, draw a border between the existing code and the new code.

Good luck! And I hope reading this will show you some options for your specific case. Also take note that you don't turn your controllers into just another facade to the database. Probably you've got the best benefits (don't know your concrete biggest problem) by using a lightweight HTTP abstraction and a view layer only as it might be that your application is for websites only.

Because as in HTTP / PHP:

[Interface In]  Plain Text HTTP request
[Application]   Free to go
[Interface Out] Plain Text HTTP response

You normally need only some functionality to parse the input and build the output.

Additionally not using fat models has the benefit that you access your data quickly and sequentially, e.g. if you don't need to pass the output at once (buffered, one-block), you can make of the benefit to stream the output to the server.

You should decide which parts are important to refactor for your application, not OOP or not. Like procedural, OOP needs to be done well as well. If you today run into problems by writing procedural code, OOP code might not the answer to your problem. The need to write better procedural code might be it. Just saying, it's not easy to refactor an application and you should identify the actual problem first.

  1. If you break it, will you even notice?
  2. If you break it, can you fix it?

The crucial part is that you can notice and that you have everything at hand to do the fix.

Get your website under test, so you can say if changing code here or there is actually doing good. Be able to turn any change back if it comes to light it's not working (better).

That done you can easily decide about a new feature or not. As long as you don't need to introduce new features, there is no need to change anything in how you write features. And until there, you can't plan for the new features.

So better think twice before re-writing your application. As written, this can kill the project.


See as well: How to implement MVC style on my PHP/SQL/HTML/CSS code?­SO Q&A

like image 119
hakre Avatar answered Oct 18 '22 15:10

hakre


OOP is all about identifying areas of responsibility and constructing self-contained units of code intended to handle one, and only one, of those areas. A general rule of thumb is that each object in your system should embody an equivalent object or concept in the real world but that's not always true as you need to also worry about abstract things that are needed to make your system work (I mean abstract here in the sense that they don't represent an item of business logic, but are still needed to make the system work. I don't mean abstract classes, which is something else altogether).

For example, in your gaming site, you're probably going to have to deal with Games, Users, Moderators, Accounts, Reviews, Comments and so on. Each of these should be a class in its own right, and every instance of that class should represent a particular User, Game, Comment and so on.

But classes have areas of responsibility, and a stated above, a class should deal with its area of responsibility and nothing else. Rendering a page is not the responsibility of any of the object classes mentioned above. This is where the classes that don't represent the entities of your system come in. You'll probably need a class for a Page, a class for a Session, a class for database connections (though PHP has you covered there already with PDO and some of the other DB modules such as mysqli).

To render a page you'd use an instance of a page class. You'd pass it a reference to the logged in user object, references to any game objects you'd want it to display, and so on. Then you'd have it render the actual HTML. The Page class doesn't need to know anything about the inner workings of the objects you pass it, other than about the APIs that those objects expose (known in OOP circles as the object's protocol, in otherwords their public methods and properties). A (very basic) Page class might look like this:

class Page
{
    private $user = NULL;
    private $games = array ();

    public function setUser (User $user)
    {
        $this -> user = $user;
    }

    public function getUser ()
    {
        return ($this -> user);
    }

    public function addGame (Game $game)
    {
        $this -> games [] = $game;
    }

    public function getGames ()
    {
        return ($this -> games);
    }

    public function generate ()
    {
        $user = $this -> getUser ();
        $games = $this -> getGames ();

        $pageFile = '/path/to/a/php/script/representing/the/page/markup.php';
        require ($pageFile);
    }

    public function __construct (User $user, array $games)
    {
        $this -> setUser ($user);
        foreach ($games as $game)
        {
            $this -> addGame ($game);
        }
    }
}

The actual markup.php script would probably look something like this:

<html>
    <head>
        <title>Games page for <?php echo ($this -> user -> getName ()); ?>
    </head>
    <body>
    <p>Hello, <?php echo ($this -> user -> getName ()), here are your games.</p>
    <?php if (count ($this -> games)) { ?>
    <ul>
        <?php foreach ($this -> games as $game) { ?>
        <li><?php echo ($game -> getName ()); ?>: your best score is <?php echo ($game -> getHighScore ($this -> user)); ?></li>
        <?php } ?>
    </ul>
    <?php } ?>
    </body>
</html>

As you might have noticed, if you use this approach then your application's modules will tend to fall into one of three categories. Your business logic objects like User and Game, your display logic like the markup.php file, and the third group that serves as a form of glue logic and coordination like the Page class.

Whilst in this particular case it's specific to your site, if you were to generalize this approach further it would fall into a design pattern known as MVC, which stands for Model, View, Controller (well actually it's closer to a pattern called PAC for Presentation, Abstraction, Controller, but it's almost always called MVC in the PHP community for some reason, so we'll just say MVC for now). A pattern is a generalization of a class of problems that programmers run into on a regular enough basis that having a toolkit of pre-made solutions is handy.

In the case of your gaming application, User and Game are models, Page is a controller, and markup.php is a view. If you substitute a different markup.php script into this code you can use it to present the exact same data in a completely different way, say as an XML file for example. You could also use the same models with a different controller to get them to do different things. The important thing to keep in mind here is that models should not concern themselves with how they are being used, that way they can be used in different ways depending on what the controller needs to achieve.

As MVC is a pattern, there are already toolkits that exist to build MVC (though they aren't really MVC ;) ) applications in PHP. These are called frameworks. There are plenty to choose from out there, such as Symfony, CodeIgnitor, Zend Framework and so on. The most popular framework these days is Zend, though I'm not personally a fan of it. (I would say that the beta versions of Zend Framework 2 do look a lot better than the current version of the framework though).

I hope this has been helpful for you. I know OOP can be daunting at first. It does require you to change your way of thinking as a programmer, but don't worry, it will come with enough practice.

like image 7
GordonM Avatar answered Oct 18 '22 14:10

GordonM