PHP - Static Methods Vs. Singleton Vs. Dependency/constructor Injection
I'm looking for some clarification here from different viewpoints to understand real world applications.
In a previous thread, I suggested to someone that they read up on singleton methods to restrict class duplication (oops!), I was quickly (and rightfully) shot down. I did this after having read through blog posts that also suggested singleton design to stop multiple MySQL connections. At the time I didn't consider that could be useful to some people.. fair enough. Thankfully I don't use singleton methods within my own code, but I do use static methods for most things. Reading through numerous blog posts, tutorials, etc.., it seems like static methods can also be considered anti-design and is something to avoid. So now it seems I'm at a point where I need to rewrite my existing framework & CMS, probably using dependency injection within my classes. I understand how this works, and why it makes sense. What I'm struggling with is understanding how to use dependency injection within a (personal) CMS application. For example - I have a config.ini file I have a class that reads the .ini file, stores the variables, and provides me methods to access them I have a content class that selects the relevant page/component from the DB (db & config dependency), then displays it via my template engine. Within the included view files I call component classes (articles, contact, etc..), each of these require a connection to the DB, which has a config dependency. Here's some code to explain it better - index.php <?php $settings = '/config/config.ini'; $config = new Config($settings); $db = new Database($config); $content = new Content( $db ); // Config may also be passed for content config - keeping it simple for example print $content->loadPage($_GET['page']); // This would now include the code below ?>Let's say that this then loads the article index (through $content->loadPage()). The view would look something like this - article_index.php <?php // Duplicated code $settings = '/config/config.ini'; $config = new Config($settings); $db = new Database($config); // Article code $articles = new Articles_Model($db); return $articles->getArticles(0,15); ?>Now my problem is that I'm duplicating the config and db class calls for no reason. Is the sollution to store these within a registry class? But then I'm creating globals, which again seems anti-design. Or is the problem how I load the active page? Any insights would be much appreciated. Similar TutorialsWas just wondering why some people choose NOT to make use of static functions when initializing objects via Factory Classes/Containers. What are the benefits of initializing the Factory class when for all intensive purposes, it's only used to initialize new classes, etc? Does this have any impact on Dependency Injection? I'm assuming that it doesn't since that would defeat the purpose. --------- Also, I've noticed that there seems to be an intense stigma within the development community in regard to singletons. Are singletons necessarily a bad thing? What about database objects? One argument I've heard is that this can often impact the flexibility of your application in the event that a new instance of said class needs to be initialized(a second completely separate connection). However, I was thinking that you could simply store these objects within a static member variable in the factory class; leaving the Database Class' __construct public in the event that you need to create that second/third/fourth connection. Wouldn't this resolve the issue? I'm trying to use dependency injection to pass a database connection to an object but I'm not sure why it's not working. I have my "dbClass" below that connects to a MySQL database. Code: [Select] class dbClass { public $db; function __construct() { $this->db = mysql_connect("localhost","username","password") or die ('Could not connect: ' . mysql_error()); return $this->db; } } Then I have my "baseClass". This is the class that I want to feed to connection too. Code: [Select] class baseClass { public $mysql_conn; function __construct($db) { $this->mysql_conn = $db; $rs = mysql_select_db("webdev_db", $this->mysql_conn) or die ('Could not connect: ' . mysql_error()); } } And this is my index.php file. The error I'm getting is "supplied argument is not a valid MySQL-Link resource". However I tripled checked and my db connection details are definately correct. Code: [Select] $db = new dbClass(); $baseclass = new baseClass($db); Thanks for any help. I need to create an entity which models the connection between a PHP server and another device so that a web app can change the entity's properties which in turn will update the server DB as well as the other device. A little background information for context is as follows: When a socket clients connects to the PHP server, it will register by providing its GUID, and in turn the server will query its DB to identify the specific client. The client has various other properties associated with the socket connection such as reconnect_timeout, response_timeout, etc, and the associated values will also be stored in the server's DB. While all socket clients use the same approach to communicate to the PHP server, several "flavors" of socket clients exists which use a specific protocol (Modbus/RTU, Modbus/IP, ControlNet, BACnet/IP, BACnet/MSTP, KNX, DALI) to communicate to downstream devices. Based on the used protocol, there will be different properties as well as different methods (i.e. some protocols allow one to query the device to retrieve a list of available data). To create the entity using inheritance, I would likely do so whe class ModbusRtuSocketClient extends ModbusSocketClient (which extends SocketClient) class ModbusIpSocketClient extends ModbusSocketClient (which extends SocketClient) class ControlNetSocketClient extends SocketClient etc.. But instead of doing so by using inheritance, I am thinking that I should do so through injection. For instance, I create an object called SocketClient which just deals with that scope of work and is protocol agnostic. I also create a second object which deals with a given protocol such as ModbusRtuProtocol extends ModbusProtocol extends Protocol. I then do one of the following: $entity = new SocketClient(new ModbusRtuProtocol()); //or $entity = new ModbusRtuProtocol(new SocketClient()); //and do $entity->setSomeSocketClientProperty(123); $entity->getProperty()->setSomeSpecificProtocolProperty(321); $entity->updateEndDevice(); $entityManager->persist($entity); $entityManager->flush(); //or do $values=$entity->queryDevice();
PS. If you think I shouldn't be doing so through injection but through inheritance, please advise.
Could use some help with dependency injection, as I can't seem to find the answer anywhere. Ok, take a quick PHP class:
<?php namespace myapp; use myapp\template; use myapp\order; class user { private $template; public function __construct(template $template) { $this->template = $template; } public function add_order(order $order) { // do something with $order here } }
I'm using the php-di package from http://php-di.org/ right now, but open to changing. My question is, how do I call that "add_order()" method, and have $order injected into it? Constructor injection is easy, and just use:
$container = new Di\Container(); $container->make(myapp\user);
How do I do that, but calling the add_order() method instead? I want something like:
$container = new Di\Container(); $container->make(myapp\user::add_order);
Any help?
Thanks, Matt
This topic has been moved to Application Design. http://www.phpfreaks.com/forums/index.php?topic=346818.0 If a class has a constructor but also has a static method, if I call the static method does the constructor run so that I can use an output from the constructor in my static method? --Kenoli I thought I was a beginner PHPer but now I'm not even sure I'm that. I have this class here which starts with: Code: [Select] private static $FormatType = ""; but then further down: Code: [Select] public function SetFormatType($NewFormatTypeId) { $this->FormatType = $NewFormatTypeId; } public function GetFormatType() { return $this->FormatType; } Do you suppose that the original developer just had a singleton or something, then latter on he found that he was making more instances and PHP tolerates this behavior? Or am I missing something? (example I looked at: http://develturk.com/tags/selfinstance/) I've looked into the singleton pattern and a few examples. I've understood most of its concept, but I still have a few things puzzling me. I noticed that you're not supposed to allow cloning (obviously) or the construction method as it shows in an example: //this object can not copy //except the getInstance() method. private function __clone() {} //prevent directly access //we want acces this only from getInstance method. private function __construct(){} Do I also have to do this for child classes, or just the parent class? I have a questions regarding Singleton Pattern in this video: http://www.youtube.com/watch?v=-uLbG7nJCJw&feature=plcp&context=C49f92beVDvjVQa1PpcFMxdGEHDoxYON2LEGpH0krK5UV1HfoVHGM= Using the singleton pattern, isn't possible to have methods that aren't static? In this video it shows nothing but static methods. Also, after creating the class object like so: $database = Database:Connect(); How would I go about using normal functions with that object? Say I had a query function. I can't do this: $database->query(); So what would I do? :/ - Thanks for any help regarding this. Hi, i made a singleton class to make connecting to the db easyer.
I got some questions.
1: is this the right way i wrote this class?
2: how can i easy implement mysqli prepare in this class?
I've looked everywhere but prepared staments need more lines of code to make it work and i don't know how to implement that in the singleton class.
<?php require_once(dirname(dirname(__FILE__)) . "/config.php"); class Database { public static $instance; private $mysqli, $query, $results, $count = 0; public static function getInstance() { if (!self::$instance) { self::$instance = new Database(); } return self::$instance; } public function __construct() { $this->mysqli = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_DATABASE); if ($this->mysqli->connect_error) { die($this->mysqli->connect_error); } } public function query($sql) { if ($this->query = $this->mysqli->query($sql)) { while ($row = $this->query->fetch_assoc()) { $this->results[] = $row; } $this->count = $this->query->num_rows; } return $this; } public function results() { return $this->results; } public function count() { return $this->count; } } ?>usage: <?php include('database.php'); $r = Database::getInstance()->query('SELECT * FROM users'); foreach ($r->results() as $row) { echo $row['name'] . '</br>'; } echo $r->count(); ?> OK, so I'm trying to create a central db class for data validation, etc. I'm starting off with the data. this code works for me, I just want to be sure that I'm not missing anything. Will dbData be accessible globally? Code: [Select] class dbData{ function regex_key_data($reg){ $z = '/[a-z\s\'{1}]*/'; $d= array( 'domain'=>'/^[a-z0-9-.]+\.[a-z0-9]{2,4}$/i', 'email'=>'/^[a-z0-9._-]+@[a-z0-9._-]+\.[a-z]+$/i', 'GUID'=>'/[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}/', 'html'=>'*htmlentities', 'int'=>'[0-9]', 'string'=>'*mysql_real_escape_string', 'fname'=>$z, 'lname'=>$z ); return $d[$reg]; } function test($reg,$val){ $z = preg_match(dbData::regex_key_data($reg),$val ,$dom); return ($z?$dom[0]:false); } } function clean_it($v,$type = 'none'){ $v = urldecode($v); if(!empty($v)){ switch($type){ case 'domain': return dbData::test('domain',$v); break; case 'email': preg_match('/^[a-z0-9._-]+@[a-z0-9._-]+\.[a-z]+$/i', $v,$email); if(empty($email[0])){return 'empty';} else{return mysql_real_escape_string($email[0]);} break; case 'GUID': return mysql_real_escape_string($v); break; case 'html': return mysql_real_escape_string(htmlentities($v)); break; case 'int': if(is_numeric($v)){return $v;} break; case 'string': return mysql_real_escape_string($v); break; } } else{ switch($type){ case 'domain': case 'none': case 'string': case 'email': case 'GUID': case 'html': return 'empty'; break; case 'int': return 0; break; } } } $br = '<br/>'; echo dbData::test('GUID','ASDFASDF-ASDF-ASDF-ASDF-ASDFASDFASDF').$br; echo dbData::test('email','asdf@asdf.asd').$br; echo clean_it('asdfsdf.com','domain'); I have a singleton class that I am revamping and need a little help with. I want to use the following syntax for my queries without having to declare a global object. Below is my current code: Code: [Select] /** * The db database object * * @access private * @var object */ private $db; /** * MySQLi database object * * @access private * @var object */ private static $instance; /** * Current result set * * @access private * @var object */ private $result; /** * The last result (processed) * * @access private * @var array */ private $last_result; /** * The number of rows from last result * * @access private * @var int */ private $row_count; /** * Last error * * @access private * @var string */ private $last_error; /** * PHP5 Constructor * * Making this function 'private' blocks this class from being directly created. * * @access private */ private function __construct() { } /** * Creates and references the db object. * * @access public * @return object MySQLi database object */ public static function instance() { if ( !self::$instance ) self::$instance = new db(); return self::$instance; } /** * Connect to the MySQL database. * * @param string $host MySQL hostname * @param string $user MySQL username * @param string $password MySQL password * @param string $name MySQL database name * @return bool True if successful, false on error. */ public function connection($host, $user, $password, $name) { // Connect to the database $this->db = new mysqli($host, $user, $password, $name); // Check connection if ( mysqli_connect_errno() ) { $this->last_error = mysqli_connect_error(); return false; } return true; } public function query($sql) { $this->result = $this->db->query($sql); return $this->result; } So then, what would I need to change in my class so that I will not have to declare a global variable for other classes and functions to use like db::query->();? Thanks in advance for your help. Well the title may seem a bit confusing, but heres an example: Code: [Select] <?php class User{ public $uid; public $username; protected $password; protected $email; public $usergroup; public $profile; public function __construct($id){ // constructor code inside } public function getemail(){ return $this->email; } public function getusergroup(){ return $this->usergroup; } public function getprofile(){ $this->profile = new UserProfile($this->uid); } } class UserProfile(){ protected $avatar; protected $bio; protected $gender; protected $favcolor; public function __construct($id){ // constructor code inside } public function formatavatar(){ // avatar formatting code inside } public function formatusername(){ // format username? } } ?> As you can see, the User class(an outer class) has a property called Profile, which can be instantiated as a UserProfile object(an inner class). The two objects have distinct functionalities, but there are times when the UserProfile object needs to access property and methods from the user object. I know its easy for outer class to access methods from inner class by using the single arrow access operator twice, but how about the other way around? Lets say from the above example the userprofile can format the username displayed to the screen by adding a sun to the left of the username if the usergroup is admin, a moon if the usergroup is mod, and nothing if its just a member. The usergroup property is stored in the outer class, and can be accessed with this $user->getusergroup() method only. I know I can always do the hard way by passing a user object to the method's argument, but is there an easier way for the inner class UserProfile to access properties/methods for outerclass User? If so, how can I achieve that? I want to create a singleton class that read multidimensional array from txt file and flatten the array retrieved. I am on a shared hosting server running CentOS. I have copied a PHP extension (imagick.so) that was built on similar server to my shared hosting server. I have resolved most of the dependencies by placing the missing files in ~/lib but I have one that I can't figure out how to cope with. I am getting the following error: I just updated a site as follows: cd /var/www/concrete5 composer update Among other changes, the following were made (more on this later): - Updating concrete5/core (8.5.2 => 8.5.4): Downloading (100%) - Updating doctrine/collections (1.6.4 => 1.6.5): Downloading (100%) - Updating doctrine/lexer (1.2.0 => 1.2.1): Downloading (100%) - Updating doctrine/inflector (1.3.1 => 1.4.3): Downloading (100%) - Updating doctrine/cache (1.10.0 => 1.10.1): Downloading (100%) - Updating doctrine/annotations (1.10.2 => 1.10.3): Downloading (100%) - Updating doctrine/common (2.12.0 => 2.13.3): Downloading (100%) - Updating doctrine/instantiator (1.3.0 => 1.3.1): Downloading (100%) - Updating doctrine/orm (v2.7.2 => v2.7.3): Downloading (100%)
errno: 150 "Foreign key constraint is incorrectly formed I expected I should have first used concrete5's update script, but too late for that. So, then I changed composer.json require concrete5/core from ^8.5 to 8.5.2 hoping to return to the previous state. Concreete5 was downgraded as desired, but now I get the following error: Class 'Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain' not found On another concrete5 site which still works, I have the following two files, however, on the broken one I only have the second: vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php:13: class MappingDriverChain extends \Doctrine\Persistence\Mapping\Driver\MappingDriverChain vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/Driver/MappingDriverChain.php:17:class MappingDriverChain implements MappingDriverSo, now I will attempt to downgrade doctrine from 2.7.3 to 2.7.2. The base composer.json file has no reference to Doctrine, but there are two other related composer files: vendor/concrete5/doctrine-xml/composer.json { "name": "concrete5/doctrine-xml", "description": "Define database structure via XML using Doctrine data types", "keywords": [ "doctrine", "xml", "structure", "database", "schema" ], "homepage": "https://github.com/concrete5/doctrine-xml", "license": "MIT", "autoload": { "psr-4": { "DoctrineXml\\": "src/" } }, "require": { "php": ">=5.3" }, "require-dev": { "doctrine/dbal": "2.5.*" } } vendor/concrete5/dependency-patches/composer.json { "type":"library", "license":"MIT", "name":"concrete5/dependency-patches", "description":"Patches required for concrete5 dependencies", "homepage":"https://github.com/concrete5/dependency-patches", "authors":[ { "name":"Michele Locati", "email":"michele@locati.it", "role":"author", "homepage":"https://mlocati.github.io" } ], "require":{ "mlocati/composer-patcher": "^1.0.0" }, "extra":{ "patches": { "doctrine/annotations:1.2.7": { "Fix access array offset on value of type null": "doctrine/annotations/access-array-offset-on-null.patch" }, "doctrine/orm:2.5.14": { "Fix UnitOfWork::createEntity()": "doctrine/orm/UnitOfWork-createEntity-continue.patch" }, "zendframework/zend-stdlib:2.7.7": { "Fix ArrayObject::unserialize()": "zendframework/zend-stdlib/ArrayObject-unserialize-continue.patch" }, "sunra/php-simple-html-dom-parser:1.5.2": { "Fix minus in regular expressions": "sunra/php-simple-html-dom-parser/minus-in-regular-expressions.patch" }, "phpunit/phpunit:4.8.36": { "Avoid each() in Getopt": "phpunit/phpunit/Getopt-each.patch" }, "tedivm/jshrink:1.1.0": { "Fix continue switch in Minifier": "tedivm/jshrink/fix-minifier-loop.patch", "Update to upstream version 1.3.2": "tedivm/jshrink/update-upstream-1.3.2.patch" }, "zendframework/zend-code:2.6.3": { "Fix continue switch in FileGenerator and MethodReflection": "zendframework/zend-code/switch-continue.patch" }, "zendframework/zend-http:2.6.0": { "Remove support for the X-Original-Url and X-Rewrite-Url headers": "zendframework/zend-http/no-x-original-url-x-rewrite.patch" }, "zendframework/zend-mail:2.7.3": { "Fix idn_to_ascii deprecation warning": "zendframework/zend-mail/fix-idn_to_ascii-deprecation-warning.patch" }, "zendframework/zend-validator:2.8.2": { "Fix idn_to_ascii/idn_to_utf8 deprecation warning": "zendframework/zend-validator/fix-idn_to_-deprecation-warning.patch" } } } } Neither seem to be applicable, but the doctrine version has to be specified somewhere. How does composer determine which version and how can I downgrade the dependency package? Thanks
PS. As a hack solution, I replaced the entire vendor/doctrine directory from one from another site, and have things working. Still, want to know how to do this right. Edited June 12, 2020 by NotionCommotionHello all, I have been trying to create a new constructor function using OOP PHP that will automatically encrypt passwords before they are sent to the database. However, I am not sure if my understanding of constructors is wrong if I am just not doing it right. As I understand it a constructor function will allow you to perform an operation on an object before the object is used. I have included some snippets of my code and would appreciate any insight on this matter: Code from the User object is below: function __construct($encrypt_pass) { $this->password = $encrypt_pass; } public function new_user() { $create_new_user = new User(); if(isset($create_new_user->id)) { $create_new_user->update(); } else { return false; } } public static function authenticate($username="", $password="") { global $db; $username = $db->escape_value($username); $password = $db->escape_value($password); $sql = "SELECT * FROM users "; $sql .= "WHERE username = '{$username}' "; $sql .= "AND password = '{$password}' "; $sql .= "LIMIT 1"; $result_array = self::find_by_sql($sql); return !empty($result_array) ? array_shift($result_array) : false; } The following code is from the administrative script to create a new user: <?php if(isset($_POST['submit'])) { $create_new_user = new User(); $create_new_user->username = trim($_POST['username']); $create_new_user->password = trim($_POST['password']); $create_new_user->first_name = trim($_POST['first_name']); $create_new_user->last_name = trim($_POST['last_name']); $create_new_user->email = trim($_POST['email']); if($create_new_user && $create_new_user->create()) { $session->message("echo $username has been successfully added as a new us$ redirect_to("index.php"); } else { $message = "There was an error while creating the new user. Please contact$ } } else { $username =""; $password =""; $first_name=""; $last_name=""; $email=""; } ?> The following snippet is from the login page: if (isset($_POST['submit'])) { $username = trim($_POST['username']); $password = trim($_POST['password']); $found_user = User::authenticate($username, $password); if ($found_user) { $session->login($found_user); log_action('Login', "{$found_user->username} logged in."); redirect_to("index.php"); } else { $message = "Username/password combination incorrect."; } } else { $username = ""; $password = ""; } ?> Since I am using the sha algorithm built into PHP I figured it would be best to build it into the object so it would occur automatically. class 1: class HelloWorld { public $world; function getHtml() { return "<html><body>". "Hello, ".$this->world."!". "</body></html>"; } } class2: class HelloWorld { public $world; function __construct($world) { $this->world = $world; } function getHtml() { return "<html><body>". "Hello ".$this->world."!". "</body></html>"; } } i don't know why in class 2 it uses a construct function. i feel it is unnecessary .any tips would be appreciated. void __construct ([ mixed $args [, $... ]] ) What is the "void" in the above code? (It looks like Java?!) TomTees Hello All, I have been having a problem recently with a constructor function that is responsible for encrypting the password every time a user logs in or creates a new account. I am using PHP OOP and the constructor function belongs to the user class. I have included the snippet below: ********************* function __construct($encrypt_pass) { $this->password = sha1($encrypt_pass); } ********************* I have the following code in the head of my register form which calls the create function once everything has been verified: <?php if ((isset($_POST['register'])) && ($_POST['email']) == ($_POST['email_verify'])) { $username = trim($_POST['username']); $password = trim($_POST['password']); $email = trim($_POST['email']); $first_name = trim($_POST['first_name']); $last_name = trim($_POST['last_name']); $create_new_user = new User($password); $create_new_user->username; $create_new_user->password; $create_new_user->email; $create_new_user->first_name; $create_new_user->last_name; $create_new_user->create(); $message = "Please check you inbox and follow the instructions to verify your account; Otherwise it will be deleted after seven days."; } elseif ((isset($_POST['register'])) && ($_POST['email']) != ($_POST['email_verify'])) { $message = "The email addresses did not match! Please enter them again."; } else { $username = ""; $password = ""; $email = ""; $email_verify = ""; $first_name = ""; $last_name = ""; } ?> ****************************** I'm not sure if I'm using the constructor correctly or not. When I instantiate the object it will give me an error unless I pass in the $password variable, $create_new_user = new User($password);, but every time I do it fails to input the other fields into the database. I know the create function is working correctly because I commented out the constructor and removed the $password variable from the initial object call and everything worked fine. Please let me know what I am doing wrong. Any help would be very much appreciated. |