Multiple class methods adding to string - php

I have a string of data that will be written to a file as a log of events, so I need this string available from the time the page begins to load to the time it is completed loading, at which time the contents of the string are written to the log file.
I am able to add to this string across some classes, but not others so it has me confused. When it doesn't work I get an 'allowed memory size of ... exhausted' error.
FILE: index.php
spl_autoload_register(function($class) {
if (file_exists(dirname(__FILE__).'/classes/'.$class.'.class.php')) {
include dirname(__FILE__).'/classes/'.$class.'.class.php';
}
});
$App = new Core();
$App->Visitor->getIP(); // This will exhaust memory...why?
$App->Settings->hello(); // Works
$App->writeLog('in my index file...'); // Works
$App->viewLog();
FILE: /classes/Core.class.php
class Core {
public static $logContent;
public function __construct() {
$this->initialize();
}
private function initialize() {
self::$logContent = 'Lets start...';
$this->Visitor = new Visitor($this);
$this->Settings = new Settings($this);
$this->Cache = new Cache($this);
}
public function writeLog($action) {
self::$logContent .= $action;
}
public function viewLog() {
echo self::$logContent;
}
}
FILE: /classes/Visitor.class.php
class Visitor {
private $App;
public function __construct($App) {
$this->App = $App;
}
public function getIP() {
$this->App->writeLog('getting ip...'); // Exhausts memory
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
} else {
return false;
}
}
}
FILE: /classes/Settings.class.php
class Settings {
private $App;
public function __construct($App) {
$this->App = $App;
}
public function hello() {
$this->App->writeLog('getting ip...');
return 'hello';
}
}
What I can't figure out is that both Visitor.class.php and Settings.class.php are setup the same way with the same constructor and yet one will work and the other won't.
So as you can see, I made a static string that everything throughout the app can add to, then later this string will be written to a file one time. Am I going about this the wrong way?

Related

PHP OOP: Create global array of messages

I am trying to display an array of messages at the end of my PHP class. My message handler is working, but only if I "add_message" from within the main parent class and not if I call this function from within a child class. Sorry if this is vague but was not sure how to word the question.
TLDR; How can I add a message from within class Example?
MAIN PARENT CLASS
class Init {
public function __construct() {
$this->load_dependencies();
$this->add_messages();
$this->add_msg_from_instance();
}
private function load_dependencies() {
require_once ROOT . 'classes/class-messages.php';
require_once ROOT . 'classes/class-example.php';
}
public function add_messages() {
$this->messages = new Message_Handler();
$this->messages->add_message( 'hello world' );
}
// I Would like to add a message from within this instance....
public function add_msg_from_instance() {
$example = new Example();
$example->fire_instance();
}
public function run() {
$this->messages->display_messages();
}
}
MESSAGE HANDLER
class Message_Handler {
public function __construct() {
$this->messages = array();
}
public function add_message( $msg ) {
$this->messages = $this->add( $this->messages, $msg );
}
private function add( $messages, $msg ) {
$messages[] = $msg;
return $messages;
}
// Final Function - Should display array of all messages
public function display_messages() {
var_dump( $this->messages );
}
}
EXAMPLE CLASS
class Example {
public function fire_instance() {
$this->messages = new Message_Handler();
$this->messages->add_message( 'Hello Universe!' ); // This message is NOT being displayed...
}
}
Because you want to keep the messages around different object, you should pass the object or use a static variable.
I would use a static variable like so:
class Init {
public function __construct() {
$this->load_dependencies();
$this->add_messages();
$this->add_msg_from_instance();
}
private function load_dependencies() {
require_once ROOT . 'classes/class-messages.php';
require_once ROOT . 'classes/class-example.php';
}
public function add_messages() {
// renamed the message handler variable for clarity
$this->message_handler = new Message_Handler();
$this->message_handler->add_message( 'hello world' );
}
// I Would like to add a message from within this instance....
public function add_msg_from_instance() {
$example = new Example();
$example->fire_instance();
}
public function run() {
$this->message_handler->display_messages();
}
}
class Message_Handler {
// use a static var to remember the messages over all objects
public static $_messages = array();
// add message to static
public function add_message( $msg ) {
self::$_messages[] = $msg;
}
// Final Function - Should display array of all messages
public function display_messages() {
var_dump( self::$_messages );
}
}
class Example {
public function fire_instance() {
// new object, same static array
$message_handler = new Message_Handler();
$message_handler->add_message( 'Hello Universe!' );
}
}
// testing...
new Init();
new Init();
$init = new Init();
$init->add_msg_from_instance();
$init->add_msg_from_instance();
$init->add_msg_from_instance();
$init->run();
Although global variables might not be the best design decision, you have at least two approaches to achieve what you want:
Use singleton.
Nowadays it is considered anti-pattern, but it is the simplest way: make message handler a singleton:
class MessageHandler
{
private static $instance;
private $messages = [];
public static function instance(): self
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
}
public function addMessage($message): self
{
$this->messages[] = $message;
return $this;
}
public function messages(): array
{
return $this->messages;
}
}
Then instead of creating a new instance of MessageHandler access it via the static method MessageHandler::instance(). Here is a demo.
Use DI container to inject the same instance (that is created once and held in the container) into all instances that need to access it. This approach is more preferable, but harder to implement in the project where there is no DI container available in the first place.

PHP __construct() inheritance not working

I have been banging my head against the wall for more than a hour now, looking for solutions on the internet (including stackoverflow), but couldn't really find any help, so I decided to ask you guys.
I have the following classes.php file
<?php
class System {
public $domain;
public function __construct() {
$this->domain = 'http://google.com';
}
public function getDomain() {
echo $this->domain;
}
}
class User extends System {
public function __construct() {
parent::__construct($this->domain);
}
public function getDomain() {
echo $this->domain;
}
}
And my code for the index.php file is:
$system = new System();
$user = new User();
$system->getDomain();
$user->getDomain();
Now, the above solution works, but it does not really what I need.
I need for the System class __construct() to be as follows:
public function __construct($domain) {
$this->domain = $domain;
}
And I want to be able to dynamically set the domain from the index.php page, like:
$system = new System('http://google.com');
So to recap everything:
I want to be able to set the domain from my constructor, like so:
public function __construct($domain) {
$this->domain = $domain;
}
instead of
public function __construct() {
$this->domain = 'http://google.com';
}
The truth is that I do not understand very well what you want to do, but I would do it this way.
class System {
private $domain;
protected function __construct($domain) {
$this->domain = $domain;
}
protected function getDomain() {
return $this->domain;
}
}
class User extends System {
public function __construct($domain) {
parent::__construct($domain);
}
}
$user = new User('http://google.com');
echo $user->getDomain();

Class autoload not working

I'm a beginner in PHP development and I'm facing a problem in my development in PHP OO. I saw is better use the autoload() function than include each file of PHP Class.
My doubt is: Why my autoload function does not work?
Follow bellow my code:
<?php
function __autoload($class)
{
include_once "model/{$class}.class.php";
}
$avaliacaoLocal = new AvaliacaoLocal();
$avaliacaoLocal->setId(1);
$avaliacaoLocal->setIdLocal(2);
$avaliacaoLocal->setComentarios("Comentários de Pedro");
$avaliacaoLocal->setIdPessoaCliente(3);
$avaliacaoLocal->setValor(5);
var_dump($avaliacaoLocal);
File AvaliacaoLocal.class.php
<?php
namespace model;
class AvaliacaoLocal
{
private $id;
private $valor;
private $comentarios;
private $idLocal;
private $idPessoaCliente;
public function __construct(){
$this->clear();
}
public function clear(){
$this->id = 0;
$this->valor = 0;
$this->comentarios = "";
$this->idLocal = null;
$this->idPessoaCliente = null;
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getValor()
{
return $this->valor;
}
public function setValor($valor)
{
$this->valor = $valor;
}
public function getComentarios()
{
return $this->comentarios;
}
public function setComentarios($comentarios)
{
$this->comentarios = $comentarios;
}
public function getIdLocal()
{
return $this->idLocal;
}
public function setIdLocal($idLocal)
{
$this->idLocal = $idLocal;
}
public function getIdPessoaCliente()
{
return $this->idPessoaCliente;
}
public function setIdPessoaCliente($idPessoaCliente)
{
$this->idPessoaCliente = $idPessoaCliente;
}
}
The error:
PHP Fatal error: Class 'AvaliacaoLocal' not found in C:\Users\Pedro
........\index.php on line 14
UPDATE:
When i use include the PHP returns the same error:
Fatal error: Class 'AvaliacaoLocal' not found in C:\Program
Files\VertrigoServ\www\system\index.php on line 10
i've change folder to verify if could be it.
The class is declared belonging to a namespace, you have to call it in this way:
$avaliacaoLocal = new \model\AvaliacaoLocal();
But now, the namespace is also included in $class, so the autoload function needs to handle that:
function __autoload($class)
{
$file = str_replace(array('_', '\\'), '/', $class) . '.php';
if (is_file($file)) {
require $file;
}
}
This function takes $class value and replace every \ (and _) from the namespace with a / to get the file name.

When I refresh the page my array is back to original, singleton not working?

It doesn't save my changes after adding a category.
If I add a category, it is seen in the overview, but if I refresh I see the original amount.
I guess there is an error in my Singleton-design but I can't seem to find it.
class ProductService {
private $_database;
public function __construct($databaseType) {
$databaseFactory = new DatabaseFactory();
$this->_database = $databaseFactory->createDatabase($databaseType);
}
public function addCategory($category){
$this->_database->addCategory($category);
}
public function getAllCategories() {
return $this->_database->getAllCategories();
}
}
class DatabaseFactory {
public function __construct() {
}
public function createDatabase($type){
switch ($type) {
case "Memory" :
return MemoryDatabase::getInstance();
}
}
}
class MemoryDatabase {
private $categories;
private function __construct() {
$this->categories = array(
new Category("Cheese"),
);
}
public static function getInstance() {
static $inst = null;
if ($inst === null) {
$inst = new MemoryDatabase();
}
return $inst;
}
private function __clone() {}
private function __wakeup() {}
public function addCategory($category) {
array_push($this->categories, $category);
}
public function getAllCategories() {
return $this->categories;
}
}
Each request you perform in PHP is stateless.
If you want to persist data between requests, you will need to put your data in some form of persistant storage, i.e., sessions, filesystem, database, memory, etc.
Singleton pattern only ensures a single copy of an object is created, for a given request.

The correct way of doing delegates or callbacks in PHP

I need to implement the following pattern in php:
class EventSubscriber
{
private $userCode;
public function __construct(&$userCode) { $this->userCode = &$userCode; }
public function Subscribe($eventHandler) { $userCode[] = $eventHandler; }
}
class Event
{
private $subscriber;
private $userCode = array();
public function __construct()
{
$this->subscriber = new Subscriber($this->userCode)
}
public function Subscriber() { return $this->subscriber; }
public function Fire()
{
foreach ($this->userCode as $eventHandler)
{
/* Here i need to execute $eventHandler */
}
}
}
class Button
{
private $eventClick;
public function __construct() { $this->eventClick = new Event(); }
public function EventClick() { return $this->eventClick->Subscriber(); }
public function Render()
{
if (/* Button was clicked */) $this->eventClick->Fire();
return '<input type="button" />';
}
}
class Page
{
private $button;
// THIS IS PRIVATE CLASS MEMBER !!!
private function ButtonClickedHandler($sender, $eventArgs)
{
echo "button was clicked";
}
public function __construct()
{
$this->button = new Button();
$this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler'));
}
...
}
what is the correct way to do so.
P.S.
I was using call_user_func for that purpose and believe it or not it was able to call private class members, but after few weeks of development i've found that it stopped working. Was it a bug in my code or was it some something else that made me think that 'call_user_func' is able call private class functions, I don't know, but now I'm looking for a simple, fast and elegant method of safely calling one's private class member from other class. I'm looking to closures right now, but have problems with '$this' inside closure...
Callbacks in PHP aren't like callbacks in most other languages. Typical languages represent callbacks as pointers, whereas PHP represents them as strings. There's no "magic" between the string or array() syntax and the call. call_user_func(array($obj, 'str')) is syntactically the same as $obj->str(). If str is private, the call will fail.
You should simply make your event handler public. This has valid semantic meaning, i.e., "intended to be called from outside my class."
This implementation choice has other interesting side effects, for example:
class Food {
static function getCallback() {
return 'self::func';
}
static function func() {}
static function go() {
call_user_func(self::getCallback()); // Calls the intended function
}
}
class Barf {
static function go() {
call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so:
} // Error -- no function 'func' in 'Barf'
}
Anyway, if someone's interested, I've found the only possible solution via ReflectionMethod. Using this method with Php 5.3.2 gives performance penalty and is 2.3 times slower than calling class member directly, and only 1.3 times slower than call_user_func method. So in my case it is absolutely acceptable. Here's the code if someone interested:
class EventArgs {
}
class EventEraser {
private $eventIndex;
private $eventErased;
private $eventHandlers;
public function __construct($eventIndex, array &$eventHandlers) {
$this->eventIndex = $eventIndex;
$this->eventHandlers = &$eventHandlers;
}
public function RemoveEventHandler() {
if (!$this->eventErased) {
unset($this->eventHandlers[$this->eventIndex]);
$this->eventErased = true;
}
}
}
class EventSubscriber {
private $eventIndex;
private $eventHandlers;
public function __construct(array &$eventHandlers) {
$this->eventIndex = 0;
$this->eventHandlers = &$eventHandlers;
}
public function AddEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex++] = $eventHandler;
}
public function AddRemovableEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex] = $eventHandler;
$result = new EventEraser($this->eventIndex++, $this->eventHandlers);
return $result;
}
}
class EventHandler {
private $owner;
private $method;
public function __construct($owner, $methodName) {
$this->owner = $owner;
$this->method = new \ReflectionMethod($owner, $methodName);
$this->method->setAccessible(true);
}
public function Invoke($sender, $eventArgs) {
$this->method->invoke($this->owner, $sender, $eventArgs);
}
}
class Event {
private $unlocked = true;
private $eventReceiver;
private $eventHandlers;
private $recursionAllowed = true;
public function __construct() {
$this->eventHandlers = array();
}
public function GetUnlocked() {
return $this->unlocked;
}
public function SetUnlocked($value) {
$this->unlocked = $value;
}
public function FireEventHandlers($sender, $eventArgs) {
if ($this->unlocked) {
//защита от рекурсии
if ($this->recursionAllowed) {
$this->recursionAllowed = false;
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->Invoke($sender, $eventArgs);
}
$this->recursionAllowed = true;
}
}
}
public function Subscriber() {
if ($this->eventReceiver == null) {
$this->eventReceiver = new EventSubscriber($this->eventHandlers);
}
return $this->eventReceiver;
}
}
As time passes, there are new ways of achieving this.
Currently PSR-14 is drafted to handle this use case.
So you might find any of these interesting:
https://packagist.org/?query=psr-14

Categories