Mixing Procedural code with OOP code — A PHP Conundrum

oop

#1

I’m gradually converting a PHP website over from procedural to OOP code. One thing I’m wrestling with is what to do with the large function library full of procedural code. I’m currently using a bunch of functions from this library within my OOP code.

⇨ Is there some way in PHP OOP to load a global function library that is accessible to all objects without having to specifically pass that function library to the object or use extend? Or is it simply…

require('functions.php');

⇨ In my OOP code I pass a database object to any object that needs to do database queries. If a function in my procedural function library needs to do a database query, do I pass on the database object to that function as a parameter? Or should i just create a new database object within that function?

⇨ Is it kosher to pass objects as a parameter to a function in a function library from within an object? And is it okay for that function to create an object and send that back to the calling object? If not, what is the best practice?

Just trying to figure out the balance of procedural vs OOP in this project.


#2

The method I used to convert procedural code to OOPs was to create two new files:

  1. index-ooo.php - new file
  2. Class_New.php - new file
  3. functions.php - existing file

Steps - one function at a time:

  1. copied and pasted functions.php -> funcName(…) to Class_New.php->funcName()
  2. renamed functions.php -> funcName(…) to functions.php -> MOVED_fnName(…)
  3. Validated, tested and repeated

file: index-ooo.php

<?php // index.php
  declare(strict_types=1); // FAIL FAST
  error_reporting(-1); 
  ini_set('display_errors', '1');

  define('LOCALHOST') 
  || 
  define('LOCALHOST', 'localhost'===$_SERVER['SERVER_NAME']);

  require 'incs/functions.php';

  require 'incs/Class_New.php';
  $jb = new Class_New;

  $dStart = date('Y-M-D');
  $dEnd   = "2020-01-01";
  echo $jb->getDaysBetween($dStart, $dEnd);

file: Class_New.php

<?php // 'incs/Class_New.php';
  declare(strict_types=1); // FAIL FAST

//==============================================
Class Class_New
{

//==============================================
PUBLIC function __construct()
# CANNOT DECLARE RETURN TYPE 
{
  # $result = 'NOT REQUIRED';
  # session_start();
    
# CANNOT DECLARE RETURN TYPE  
  # return $result;
}

//==============================================
public function getDaysBetween(string $start, $end="2020-01-01")
:string
{
  $start  = strtotime($start);
  $bDay   = strtotime($end);
  $diff   = $start - $bDay;
  $sDays  = number_format ( -1 * round($diff / (60*60*24) ) );

  return $sDays;
}// endfunc


//===========================================================
// Usage:
//   $mysqli =  $this-> getDbConnection();
//===========================================================
private function getDbConnection()
:mysqli
{
  if(LOCALHOST):
    $mysqli = new mysqli("localhost", "john", "betong", "dbName");
  else:
    $mysqli = new mysqli("localhost", 'john', "Betong", "dbName");
  endif;  

# check connection 
  if (mysqli_connect_errno())
  {
      echo sprintf("<br>Connect failed: %s\n", mysqli_connect_error() );
      exit();
  }

  return $mysqli;
}

}//end class

file: functions.php

<?php // 'incs/functions.php';
  declare(strict_types=1); // FAIL FAST

//==============================================
function MOVED_getDaysBetween($start, $end="2020-01-01")
:string
{
 #$now    = time(); // or your date as well
  $start  = strtotime($start);
  $bDay   = strtotime($end);
  $diff   = $start - $bDay;
  $sDays  = number_format ( -1 * round($diff / (60*60*24) ) );

  return $sDays;
}

Edit:
Added Class_New -> private function getDbConnection()


#3

Yes, require, or even better require_once can be used. If you’re using composer you could also add the file to be loaded always in your composer.json. See https://getcomposer.org/doc/04-schema.md#files.

If you’re not using composer you should.

You should pass the database as a parameter. Don’t use globals or stuff like service locators, they will just mess you up in the long term.
Certainly do not create a database within the function, the function shouldn’t know how to connect to a database. It should only know how to use an existing connection.

Of course over time all these functions must be removed and proper objects created that perform the same actions in an OOP manner.

That’s certainly fine, as long as creating those objects doesn’t have any side effects. But that’s a basic requirement for creating any object really.
I would chalk it up as technical dept though and at one point move that functions functionality into the caller or create a collaborating class.


#4

@John_Betong, I don’t mean to be rude, but just stuffing a bunch of functions in a class doesn’t make it OOP. The main point of OOP is encapsulation, meaning you create different classes that are all responsible for a part of the application and none of the classes is allowed to know how other classes work internally.

Your class already has three concerns, one to create a mysqli connection for development, one to create a mysqli connection for production, and some function to do something with dates.

The date stuff must certainly be extracted to a separate class, as it has nothing to do with database connections.

Then the class should get passed the parameters for the database connection and use those to create a mysqli object when needed. It should not have all credentials for all environments hardcoded. First of all you should never hard code passwords, they should be injected per environment, and second it violates the open/closed principle.

Even better, you should not be able to ask it for a mysqli object, but you should be able to pass it queries and it should return the answers, so the outside world isn’t even aware it is using mysqli. This gives you the leverage to at one day replace mysqli with PDO for example with in just that single class, since the contract with the outside world (query in, array out) will still be the same.


#5

No problems about being rude, I thought I would start the ball rolling :slight_smile:

From the original post it is only possible to guess at the complexity and also to determine how many classes are required.

My post was a start to remove the existing functions from the function.php file. Once or while all functions are moved it may be beneficial to introduce separate classes.


#6

Why? I’ve looked at Composer, but seems like an extra level of unnecessary complexity.

Really? This is what I don’t understand about OOP in PHP. Here is a simple function I use in nearly every script:

function cleanData($data) {
	return trim(strip_tags($data));
}

This is simplified from the original, but you get the picture. Are you saying this should be turned into its own class and then loaded as an object, and then passed to whatever object needs that function? That just seems way more inefficient than a simple function library that I can easily call from within any script or object. This would mean some of my primary objects would need a dozen or more of these “function objects” passed into them.

But perhaps it’s because I simply don’t understand what you mean, or how to implement this in OOP properly. It’s been a tough slog trying to wrap my head around OOP concepts for the past 1+ year, and when I think I finally understand, something like this comes along to boggle my addled brain.


#7

Hi John, thanks for recommendation.

What am I supposed to do with Class_New.php? How do I make the functions/methods in that class available to all of my scripts? Do I have to pass that into every object that will need those methods? Seems like a lot more hassle than a simple functions library, what am I missing? I already have to pass about 3-4 utility objects (like Database.php) to the objects that load content for this PHP website app.


#8

As @ScallioXTX pointed out my proposed class was only a starter and should be split into multiple classes and inheritance, etc used.

To simplify the index.php try this:

$dStart = date('Y-M-D');
$dEnd   = "2020-01-01";

if(true): // toggle on/off
  require 'incs/functions.php';
  echo getDaysBetween($dStart, $dEnd);
else:
  require 'incs/Class_New.php';
  $jb = new Class_New;
  echo $jb->getDaysBetween($dStart, $dEnd);
endif;

Try copying the two files and see if you can get them to work.

I hope to be on my desktop in about an hour to respond to any problems.


#9

I started writing code in BASIC and have a deeply ingrained “procedural think” mindset. I confess that although I can use OOP and write OOP syntax without any problems, when it comes to writing my own OOP it tends to be “procedural in OOP clothing”. i.e. simply making a function a class method does not in itself make it OOP.

What helps me understand OOP the most is when I write Java code. It forces me to think about public-protected-private, class-interface-inherit-extend, i.e. what level of control is needed, reusability, dependency concerns, organization, separation of concerns etc.

IMHO, if a collection of functions works for you, simply making them class methods for the sake of calling the code OOP doesn’t make much sense. On the other hand, the more you work with OOP the better you should be able to get at it.


#10

It took me several years to grasp OOP and then literally in a day it just clicked. It was like all of a sudden someone flipped on the OOP switch. I still dont have the higher levels mastered yet such as design patterns and ORM’s but that will come in time as well. Just keep at it. We were all beginners at one time.


#11

I think no one has mentioned static methods above.
Potentially you could have a class with static utility methods that you are allowed to call without creating a class instance.

For example:

<?php 

class Utils {

    public static function myUtil() {
        //code...
    }
}

// then from another file you include your class
include "/path/to/my/utils/utils.php"
Utils::myUtil();


#12

I have a very basic knowledge of PHP Classes and hope to get better as time progresses and also hope @benanamen’s Eureka moment when “it just clicked” comes a a lot sooner than “several years to grasp”.

Now that I have started using PHP Classes I far prefer them to basic functions because the methods are all grouped together. I also endeavour to use Class Private functions (and $this->_function()) whenever possible because error checking immediately halts and when incorrectly used.

Also useful is the Extends feature when combining two separate classes:

http://php.net/manual/it/keyword.extends.php


#13

To be honest, I had numerous spurts when I would spend time trying to learn OOP. If I had been more dedicated to learning it, it wouldn’t have taken near as long. My hump to get over in my head was “Why should I write all this complicated code when I can do the same thing in procedural with less code”. Now that I understand it, I don’t want to code any other way. I actually put a halt to my pet projects and started rewriting them in OOP.

My first struggle with it was getting it in my head that functions are now methods and variables are now properties. Seems ridiculously stupid now, but it took time to automatically translate in my thinking. A lot like learning a spoken language. It starts off as head knowledge that you have to consciously translate, but at some point you just say “Hola Amigo” without doing a translation in your head.

Then there was $this. Oh what a struggle that was. If only I had read @TomB’s book " PHP MySQL: Novice to Ninja". He does a great job at starting with the basics and building you up along the way. I have been coding nearly 30 years and have learned numerous things from the book. I highly recommend everyone read it no matter what your skill level is.


#14

I got the feeling that static methods are to be avoided in OOP for some reason.


#15

Thanks for all your helpful input, guys! But I’m still a bit lost. So here’s a more specific example, and maybe you can tell me how you’d approach this in OOP.

This is a simplified version of a class I use. It simply loads News content for a client website into an array so it can be passed to a template to generate an HTML page that shows a list of news articles.

class ClientNews 
{
	protected $db;
	protected $site;
	protected $widget;
	protected $client_id = 0;

	public function __construct(DB $db, Site $site, Widget $widget=null)
	{
		$this->db = $db;
		$this->site = $site;
		$this->widget = $widget;
	}

	public function setClientId($client_id)
	{
		if (!is_numeric($client_id)) {
			// If alpha ID passed, look up numeric ID for client
			$result = $this->db->select("SELECT id FROM clients WHERE client.alphaid = '$client_id' LIMIT 1");
			$this->client_id = $result['id'];
		} else {
			$this->client_id = $client_id;
		}
	}

	public function getNews() 
	{
		// This is where a query is built using the client_id in a WHERE statement if one specified. 
		// The query is also built using info from the $site and $widget objects passed in.

		$query = "SELECT ....";

		$records = $this->db->select($query);

		return $records; 
	}
}

The method setClientId() is used in a bunch of other classes that load various types of other content for a client website. Right now I’ve simply duplicated this method in each class that needs it, but that doesn’t seem right. Previously I would have made setClientId() a function in a global function library that would be included in every script. Easy! But I have no idea how this should be handled in OOP.

My first thought is that I should create a new class called ClientID.php and put the small snippet of code in there. But then that would mean I’d have to pass that into the ClientNews class, which means it would be a 4th dependency: $db (my Database class), $site (contains info about the client website), $widget (contains settings for the content widget, in this case News), and then $clientid.

But it won’t end there, if I do this with other functions I’m currently using from my procedural function library I could potentially need to pass a few more dependencies into this object to make it work. This seems a bit ridiculous to me. So I’m hoping this is not the right OOP solution! :slight_smile:

How would you handle this in OOP?


#16

For many reasons https://r.je/static-methods-bad-practice.html

But, a static method is not any different in this case to a standard function outside the class, so it makes little difference here.

You should consider making a Client class:

class Client {
	private $id;
	private $db;

	public function __construct(DB $db, $id) {
		$this->db = $db;
		$this->id = $id;
	}

	public function getId() {
		if (!is_numeric($this->id)) {
			// If alpha ID passed, look up numeric ID for client

			$result = $this->db->select("SELECT id FROM clients WHERE client.alphaid = '$client_id' LIMIT 1");

			//update the ID from the database so if this method is called twice the query only happens once
			$this->id = $result['id'];
		} 

		return $this->id;
	}
	
}

public function getNews(Client $client) 
	{
		// This is where a query is built using the client_id in a WHERE statement if one specified. 
		// The query is also built using info from the $site and $widget objects passed in.

		// use $client->getId() wherever you need it

		$query = "SELECT ....";

		$records = $this->db->select($query);

		return $records; 
	}


$news = $clientNews->getNews(new Client($db, 'ABCD'));

also: Read up on SQL injection


#17

Thanks, Tom! That’s what I remember about static functions, to avoid them as much as possible, and so I have.

I’d be interested to get your feedback to the post I made right above yours if you have time.


#18

Sorry I sneakily edited my post to answer that!


#19

Just saw your edit, thanks!

Okay, that makes sense to me, and I considered doing that as well. But the getNews() method actually needs several other objects passed in to make the query work, as indicated in my sample code above: $db, $site, $widget, and now $client. I need a bit of info from each of those classes to build the query in the getNews() method.

Does that make any difference to your solution? How many dependencies is too many for an object?


#20

There’s no strict answer to this but you’re definitely right to question it. If you have a lot of if/else logic you should probably use polymorphism in its place: https://refactoring.guru/replace-conditional-with-polymorphism

Here’s a similar guide I wrote: https://r.je/single-responsibility-principle-how-to-apply.html