Viewed   82 times

I'm not new to PHP or programming at all. But recently I was thinking about website programming in PHP and how easier it was before the OOP. Anyway, I prefer OOP than the old procedural style. I want to implement a website but it seems I always have to use a global or a static variables. And I'm starting to wonder, how can I do it without those?

Anyway, what I'm talking about is having a class for each "component" of the website. For example if it was an url shortener website it would be: links, members, database.

What I'm talking about is way more complicated, at least 8 classes. Anyway, my current approach is the following:

$database = new Database(...);
$links = new Links($db);
$users = new Users($db);

Anyway, for example I want to get all the links a user posted by its ID, I need to use both links and both users components.

Is there any other way I could do this? Any other approach? except passing them as constructor parameters.

 Answers

3

You should have the following components:

  1. Business objects, which model and express one particular "thing" in your app:

    class Link { ... }
    class User { ... }
    

    These don't "do" anything, they're just there to formalise your data structures. These objects have getter and setter methods to get and set individual attributes, which are also validated there:

    public function setUrl($url) {
        if (!/* validate the URL here*/) {
            throw new InvalidArgumentException("$url is not a valid URL");
        }
        $this->url = $url;
    }
    

    Minimum required data is part of the constructor. This ensures your data integrity application-wide. It allows you to assert that when you have an instance of Link, the data expressed by it is minimum valid data for a link.

  2. A database link. Only the bare necessary thing to connect to a database, nothing more, nothing less. A raw PDO or mysqli object will do just fine.

  3. A data-object mapper, which takes a database link and knows how to store business objects in the database and how to retrieve them:

    class LinkStorage {
    
        protected $db;
    
        public function __construct(PDO $db) {
            $this->db = $db;
        }
    
    }
    

    This class has all the various methods of how to retrieve things from your database:

    public function getById($id) {
        $stmt = $this->db->prepare('SELECT ... FROM ... WHERE id = :id');
        $stmt->execute(compact('id'));
    
        if (!$data = $stmt->fetch()) {
            throw new RuntimeException("Record with id $id does not exist");
        }
    
        return new Link($data['url']);
    }
    

    You can have all sorts of different queries encapsulated this way, e.g.:

    /**
     * Returns all links by a particular user.
     * @param User $user
     * @return Link[]
     */
    public function getAllFromUser(User $user) {
        ...
    }
    

The usage is then simple:

$db          = new PDO(...);
$linkStorage = new LinkStorage($db);
$userStorage = new UserStorage($db);

$user  = $userStorage->getById($id);
$links = $linkStorage->getAllFromUser($user);

This kind of code would then be encapsulated in a service class, which holds all the possible "actions" you can do in your app. registerUser(array $data), getLinksOfUser($id), newLinkFromPostData(array $data) etc.

What I've just described is basically the model portion of an MVC-style application. The other two parts would be controllers which call the service methods, and views which output data retrieved from service methods. This approach keeps responsibilities separate and isolated and allows you to put higher-level logic and functionality together like building blocks. Business objects are the lowest building block, their structure needs to be solid and well defined for the rest to work. Data-object mappers just concern themselves with putting those objects into the database and getting them back out again. Services then put all this together in various complex ways and make things happen.

You shouldn't have any cyclic dependencies with this, as responsibilities are well separated. Your individual dependencies may still be somewhat complex. If it becomes too cumbersome to instantiate classes, you'll want to look into Factories:

$factory = new Factory;
$userStorage = $factory->newUserStorage();

All the complexity of instantiation is encapsulated in this factory. One step further are dependency injection containers, who you can configure in, for example, an XML file to specify which class depends on what, and then the DI container will take care of it for you.

Monday, October 3, 2022
5

Background: You asked for a "simple explanation" which suggests:

  1. You want a no-nonsense overview without jargon
  2. You want something that will help you learn from the beginning
  3. You have discovered that no two people ever answer the question the same way, and it's confusing. That's the reason you are here asking for a simple explanation. Yes?

Short No-Jargon Answer:

  1. Many introductory explanations jump quickly into "OOP real world" examples. Those can tend to confuse more than help, so feel free to ignore that for now.
  2. You can think of source code simply as "chunks" of functionality, that just happen to be saved to individual files.
  3. There are different ways of organizing those "chunks"; depending on things like conventions of the programming language, the background and training of the developer(s), or just plain old personal preference.
  4. OOP and Procedural programming are simply two main, generally-recognized methodologies, for how to organize and arrange those "chunks" of code.

Long No-Jargon Answer:

Procedural vs OOP is just one aspect of a fundamental issue of computer programming: how to make your code easy to understand and a piece of cake to professionally maintain. You can actually write "Procedural" code that follows some of the principles of OOP, so the two are not necessarily opposites.

Your understanding will really grow once you learn other object-oriented programming languages, among which, PHP is a "new kid on the block".

Here is a quick overview of what you will learn as you build experience:

  • You can write PHP source code that does useful tasks

  • You can organize useful tasks into "chunks" of code

  • You can think of "chunks" of code independently of the individual files where they are saved

  • Sometimes those "chunks" of code will behave differently based on parameters you pass in

  • Chunks of code that accept parameters are called "Functions"

  • Functions can be "chunked" together, and there are different ways of doing this:

    • For example: you could have just one big PHP file with all the functions you have ever written in your entire life, listed in alphabetical order by function name
    • For example: you could have multiple PHP files with functions that are chunked together by subject matter [e.g., functions for doing basic string manipulation, functions for processing arrays, functions for file input/output, etc]
  • OOP is a special way of "chunking" Functions together into a "Class"

  • A Class is just another level of "chunking" code together so that you can treat it as a unified whole

  • A Class can be thought of as a "chunking" of methods and properties

    • methods are simply functions that are logically related to one another in some meaningful way. The words "method" and "function" are basically two different terms for the same thing.
    • properties are simply data values that are related to the class. These are values that are intentionally non-isolated to any individual function, because more than one of the functions in the class should have access to them.
      • For example: if your class has a bunch of methods for doing astronomy, properties of the class might be the values for certain famous numbers that all astronomy methods need to know about (like Pi, the speed of light, the distance between specific planets, etc.).
    • This is where most OOP explanations get confusing because they branch off into "real world examples" which can quickly get off-topic. Often, "real world" is a euphemism for the ontological perspectives of a particular individual or group. That tends to be useful only once you already understand the concept well enough to teach it to someone else.
    • To understand OOP without confusion, you can skip the "real world" examples for now, and just focus on the code. A Class is simply a way to store functions (aka methods) and properties (aka data) as PHP code in one or more related "chunks" where each individual "chunk" deals with a specific topic or piece of functionality. That's all you need to know in order to get started.
  • A Class is useful because it allows you to organize your code at a very high level in a way that makes it easy for you to understand, use, and maintain.

  • When someone has written a lot of functions, and organized them into a lot of Classes, and gotten those to work together in some cool way, they package the whole thing together and call it a "Framework".

  • A Framework is just the next-highest level of "chunking" (including coding style and conventions) that one or more people agree on because they like the way the code is organized and it suits their working style, preferences, values, plans for world domination, etc.

See also

  • OOP appeal
Thursday, September 15, 2022
2

In a lot of scenarios, procedural programming is just fine. Using OO for the sake of using it is useless, especially if you're just going to end up with POD objects (plain-old-data).

The power of OO comes mainly from inheritance and polymorphism. If you use classes, but never use either of those two concepts, you probably don't need to be using a class in the first place.

One of the nicest places IMO that OO shines in, is allowing you to get rid of switch-on-type code. Consider:

function drive($the_car){

    switch($the_car){

      case 'ferrari':
          $all_cars->run_ferrari_code();
          break;

      case 'mazerati':
          $all_cars->run_mazerati_code();
          break;

      case 'bentley':
          $all_cars->run_bentley_code();
          break;
    }
}

with its OO alternative:

function drive($the_car){

    $the_car->drive();
}

Polymorphism will allow the proper type of "driving" to happen, based on runtime information.


Notes on polymorphism:

The second example here has some premisses: That is that all car classes will either extend an abstract class or implement an interface.

Both allow you to force extending or implementing classes to define a specific function, such as drive(). This is very powerful as it allows you to drive() all cars without having to know which one you're driving; that is because they're extending an abstract class containing the drive() method or implementing an interface forcing the drive() method to be defined.

So as long as you make sure that all your specific cars either extend the abstract class car or implement an interface such as canBeDriven (both of which must declare the drive() method) you can just call the drive() method on an object which you know is a car (but not what type of car) without fear of it not being defined, as PHP will throw fatal errors at you until you define those methods in your specific car classes.

Thursday, September 1, 2022
 
2

Instead of automatically loading more list-items, I suggest placing a button users can tap to load more (but that's just my suggestion).

//setup an interval so we can throttle the `scroll` event handler since there will be tons of `scroll` events fired
var timer    = setInterval(function () {
        scrollOK = true;
    }, 100),//run this every tenth of a second
    scrollOK = true;//setup flag to check if it's OK to run the event handler
$(window).bind('scroll', function () {

    //check if it's OK to run code
    if (scrollOK) {

        //set flag so the interval has to reset it to run this event handler again
        scrollOK = false;

        //check if the user has scrolled within 100px of the bottom of the page
        if ($(this).scrollTop() + $(this).height() >= ($(document).height() - 100)) {
            //now load more list-items because the user is within 100px of the bottom of the page
        }
    }
});

Here is a demo: http://jsfiddle.net/knuTW/

Update

It's a bit easier to just load a button that the user can tap, then when it's tapped, load more rows and then re-append the load-more button to the end of the list:

var $loadMore = $('ul').children('.load-more');
$loadMore.bind('click', function () {
    var out = [];
    for (var i = 0; i < 10; i++) {
        out.push('<li>' + (count++) + '</li>');
    }
    $('ul').append(out.join('')).append($loadMore).listview('refresh');
});‚Äč

Here is a demo: http://jsfiddle.net/knuTW/2/

Thursday, December 1, 2022
 
5

You'll probably want to write your source code to use a #defined constant version string. You can then pass that in through your build with a -DMY_VERSION=... option. That'll let you embed a default value in the code, too, wrapped in an #ifndef, just in case!

#ifndef MY_VERSION
#define MY_VERSION 0.0.1-alpha
#endif

print_version() {
    printf("my product: %sn", MY_VERSION);
}

A nice way to handle this on the build process side to make an intermediate build product which is simply a makefile snippet like MY_VERSION = "...". This again adds redundancy by letting you distribute the project with the version file already created, so that the build doesn't have to depend on the presence of the SCM.

You can then create the version string however you like, for example:

echo -n 'MY_VERSION = "' > VERSION_FILE
git describe >> VERSION_FILE
echo "Compiled on $(date)" >> VERSION_FILE
...
echo '"' >> VERSION_FILE

Then in your primary makefile, include that snippet, and add -DMY_VERSION='"$(MY_VERSION)"' to the build flags for the appropriate object.

A slight variation: make your generated file purely the version string, then pull that value into the appropriate variable in the makefile.

If you need help with specific git commands to get the desired output, feel free to comment. git describe is a great one, though, meant for exactly this kind of thing. The default output is the closest tag ancestor of the current commit, hyphen, number of commits since the tag, hyphen, and abbreviated commit hash.

Tuesday, October 25, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :