Argument value is not being passed in PHP classes - php

I have a scenario where I want to create a user using Object Oriented PHP8, I have my users.php page where I have the form and this issue seems to be that I do not think the method from my object form ($objForm) are being passed in correctly to my object validation ($objValidation):
users.php
<?php
use A\B\App\Session;
use A\B\App\Login;
use A\B\App\User;
use A\B\App\Form;
use A\B\App\Validation;
require_once("../../vendor/autoload.php");
$session = new Session();
if(!$session->isLogged()) {
Login::redirectTo("login");
}
$objForm = new Form();
// passing in the object to validation
$objValidation = new Validation($objForm);
$objUser = new User();
if($objForm->isPost("name")) {
$objValidation->expected = ["name"];
$objValidation->required = ["name"];
$objValidation->special = ["email" => "email"];
$objValidation->postFormat = ["password" => "password"];
$email = $objForm->getPost("email");
$existingUser = $objUser->getUserByEmail($email);
if($existingUser) {
$objValidation->addToErrors("user_exists");
}
if($objValidation->isValid()) {
echo "good";
} else {
echo "bad";
}
}
?>
<form action="" method="post">
<p>
<input type="text" name="name" placeholder="User name" required="">
</p>
<?php echo $objValidation->validate('user_exists'); ?>
<p>
<input type="email" name="email" placeholder="User email" required="">
</p>
<p>
<input type="password" name="password" placeholder="Password" required="">
</p>
<p>
<input type="submit" name="" value="Add user">
</p>
</form>
I am simply trying to echo out a yes or no to see if it passes this point so I am not actually calling a method to create a user yet since the code is failing before.
below are my 2 classes:
Form.php
<?php
declare(strict_types = 1);
namespace A\B\App;
class Form
{
public function isPost(string $field): bool
{
if(!empty($field)) {
if(isset($_POST[$field])) {
return true;
}
return false;
} else {
if(!empty($_POST)) {
return true;
}
return false;
}
}
public function getPost(string $field)
{
if(!empty($field)) {
return $this->isPost($field) ? strip_tags($_POST[$field]) : null;
}
}
public function getPostArray(array $field): array
{
$out = [];
if($this->isPost()) {
foreach($_POST as $key => $value) {
if(!empty($field)) {
if(in_array($key, $expected)) {
$out[$key] = strip_tags($value);
}
} else {
$out[$key] = strip_tags($value);
}
}
}
return $out;
}
}
and here is the validation class:
Validation.php
<?php
declare(strict_types = 1);
namespace A\B\App;
class Validation
{
private array $error = [];
public array $message = ["user_exists" => "This user exists in the Db"];
public array $expected = [];
public array $required = [];
public array $post = [];
private $objForm;
public array $test = [];
public array $postRemove = [];
public array $postFormat = [];
public array $special = [];
public function __construct(Form $form)
{
$this->objForm = $form;
}
public function process()
{
if($this->objForm->isPost() && !empty($this->required)) {
$this->post = $this->objForm->getPostArray($this->expected);
if(!empty($this->post)) {
foreach($this->post as $key => $value) {
$this->check($key, $value);
}
}
}
}
public function check($key, $value)
{
if(!empty($this->special) && array_key_exists($key, $this->special)) {
$this->checkSpecial($key, $value);
} else {
if(in_array($key, $this->required) && empty($value)) {
$this->addToErrors($key);
}
}
}
public function checkSpecial($key, $value)
{
switch($this->special[$key]) {
case 'email':
if(!$this->isEmail($value)) {
$this->addToErrors($key);
}
break;
}
}
public function isEmail($email)
{
if(!empty($email)) {
$result = filter_var($email, FILTER_VALIDATE_EMAIL);
return !$result ? false : true;
}
return false;
}
public function isValid()
{
$this->process();
if(empty($this->errors) && !empty($this->post)) {
// remove unwanted fields
if(!empty($this->postRemove)) {
foreach($this->postRemove as $value) {
unset($this->post[$value]);
}
}
// format required fields
if(!empty($this->postFormat)) {
foreach($this->postFormat as $key => $value) {
$this->format($key, $value);
}
}
return true;
}
return false;
}
public function format($key, $value)
{
switch($value) {
case 'password':
$this->post[$key] = Login::passwordEncrypt($this->post[$key]);
break;
}
}
public function addToErrors($key)
{
$this->errors[] = $key;
}
public function validate($key)
{
if(!empty($this->errors) && in_array($key, $this->errors)) {
return $this->wrapWarn($this->message[$key]);
}
}
public function wrapWarn($mess)
{
if(!empty($mess)) {
return "<span class=\"warn\" >{$mess}</span>";
}
}
}
I have tested other dummy functions from validations.php and form.php and both work so the issue is definitely between the communication with these 2 classes. The process method is not recognizing or not getting values from the $this->objForm->isPost() call. Here is the error message I get where its telling me no value is being passed in:
Fatal error: Uncaught ArgumentCountError: Too few arguments to
function A\B\App\Form::isPost(), 0 passed in
/Applications/MAMP/htdocs/project/src/App/Validation.php on line 41
and exactly 1 expected in
/Applications/MAMP/htdocs/project/src/App/Form.php:10 Stack trace:
#0 /Applications/MAMP/htdocs/project/src/App/Validation.php(41): A\B\App\Form->isPost() #1
/Applications/MAMP/htdocs/project/src/App/Validation.php(94):
A\B\App\Validation->process() #2
/Applications/MAMP/htdocs/project/src/Pages/users.php(36):
A\B\App\Validation->isValid() #3 {main} thrown in
/Applications/MAMP/htdocs/project/src/App/Form.php on line 10
Note: I have not been applying type declarations/hints to everything yet because of being stuck.

In order to not change too much the codebase (change all the references of the method), you should change the signature of the isPost() function in this way:
public function isPost(string $field = ""): bool

Related

PHP - What is wrong? Learning MVC - Beginner

Hy,
i started learning PHP and i created a simple MVC Style Codebase.
The Script just generates a random number and displays this numer. I also write a function to display the number shown before but it does not work. The value is empty. Can you help me out, i have no clue whats wrong and there is no php error thrown.
view.php
<?php
class View
{
private $model;
private $view;
public function __construct()
{
$this->model = new Model();
}
public function output()
{
echo 'Current Entry: ';
echo $this->model->getData();
echo '<br />';
echo 'Update';
echo '<br />';
echo 'Last';
}
public function getModel()
{
return $this->model;
}
}
controller.php
<?php
class Controller
{
private $model;
private $view;
public function __construct($view)
{
$this->view = $view;
$this->model = $this->view->getModel();
}
public function get($request)
{
if (isset($request['action']))
{
if ($request['action'] === 'update')
{
for ($i = 0; $i<6; $i++)
{
$a .= mt_rand(0,9);
}
$this->model->setData($a);
}
elseif ($request['action'] === 'preview')
{
$this->model->setLast();
}
else
{
$this->model->setData('Wrong Action');
}
}
else
{
$this->model->setData('Bad Request');
}
}
}
model.php
<?php
class Model
{
private $data;
private $last;
public function __construct()
{
$this->data = 'Default';
}
public function setData($set)
{
if ( ! (($set == 'Wrong Action') && ($set == 'Bad Request')))
{
$this->last = $this->data;
}
$this->data = $set;
}
public function getData()
{
return $this->data;
}
public function setLast()
{
$this->data = $this->last;
}
public function getLast()
{
return $this->last;
}
}
index.php
<?php
require_once 'controller.php';
require_once 'view.php';
require_once 'model.php';
$view = new View();
$controller = new Controller($view);
if (isset($_GET) && !empty($_GET)) {
$controller->get($_GET);
}
$view->output();
Are there any other, bad mistakes in the Script?
Any input very welcome! :)
The problem with your code is that PHP does not preserve variable values between requests, therefore, when you set your $model->last value here:
$this->last = $this->data;
It gets reset on your next request.
You may want to store $last value in a session or a cookie instead. Something like:
$_SESSION['last'] = $this->data;
And then when you are instantiating your model you could initialize it with a value stored in a session if available:
index.php - add session_start() at the beginning
model.php:
public function __construct()
{
$this->data = isset($_SESSION['last']) ? $_SESSION['last'] : 'Default';
}
public function setData($set)
{
$this->data = $set;
if ( ! (($set == 'Wrong Action') && ($set == 'Bad Request')))
{
$_SESSION['last'] = $this->data;
}
}
controller.php
elseif ($request['action'] === 'preview')
{
//Remove this
//$this->model->setLast();
}

Php: turning it into a recursive function

I have currently two classes.
the ArrayCompare class:
<?php
namespace App\Tools\RegexExtract;
class ArrayCompare
{
public function compare(Array $arrayToCompare)
{
$elementData = new ElementMetaData();
$metaData = $elementData->extract($arrayToCompare[0], [], $initial=true);
foreach ($arrayToCompare as $currentElement) {
$metaData = $elementData->extract($currentElement, $metaData);
}
return $metaData;
}
}
which uses the ElementMetaData class
<?php
/**
* A class for extracting meta data from an element.
*/
namespace App\Tools\RegexExtract;
class ElementMetaData
{
public function extract($element, $metaDataToCompare = [], $initial = false)
{
if ($initial == true) {
$this->isInteger($element) ? $returnMetaData['isInteger'] = $this->isInteger($element) : null;
$returnMetaData['length'] = $this->length($element);
}
else {
$returnMetaData=$metaDataToCompare;
if ($returnMetaData != []) {
if (isset ($returnMetaData['isInteger']) && !$this->isInteger($element)) {
unset($returnMetaData['isInteger']);
}
if (isset ($returnMetaData['length']) && $this->length($element) != $returnMetaData['length']) {
unset($returnMetaData['length']);
}
}
}
return $returnMetaData;
}
private function isInteger($element)
{
return is_int($element);
}
private function length($element)
{
return strlen($element);
}
}
the basic functionality is:
given I have an array
$arr=[1,2,3];
I want to get the "similarities" between ALL Elements. According to a an array i Predefine...so this would deliver this result:
$metaArray=['isInteger'=>true,'length'=>1];
and this would deliver just length as similarity:
$arr=[1,2,'D'];
$metaArray=['length'=>1];
While this array would deliver an empty result []
$arr=[1,2,'3D']; // result is [] since not all integers or not all of same length.
Now my solution does not use recursive functions...but I am sure it can be used somehow.
Also, I want to add more "criteria"....So "isEmailAdress", "beginswithA"....etc....and this would make my if statements a horror....so what is the best strategy/design pattern to follow here?
#deceze beat me to it by fair margin... but I'll still post my solution that works basically with the same principles.
abstract class abstractComparer
{
private $array;
private $result = true;
protected $name;
public function compareArray($array)
{
$current = null;
foreach ($array as $index => $value)
{
$this->result = $this->result && $this->compareValues($index, $current, $value);
$current = $value;
}
}
public function getResult()
{
return $this->result;
}
public function getName()
{
return $this->name;
}
public abstract function compareValues($index, $value1, $value2);
public abstract function getSuccessValue();
}
class intComparer extends abstractComparer
{
protected $name = "isInteger";
public function compareValues($index, $value1, $value2)
{
return is_int($value2);
}
public function getSuccessValue()
{
return true;
}
}
class lengthComparer extends abstractComparer
{
protected $name = "length";
protected $length = 0;
public function compareValues($index, $value1, $value2)
{
$this->length = strlen($value2);
return $index == 0 || strlen($value1) == $this->length;
}
public function getSuccessValue()
{
return $this->length;
}
}
And do the actual processing like this:
$temp = [1,2,3];
$comparers = [new intComparer(), new lengthComparer()];
$result = array();
foreach ($comparers as $comparer)
{
$comparer->compareArray($temp);
if ($comparer->getResult())
{
$result[$comparer->getName()] = $comparer->getSuccessValue();
}
}
//var_dump($result);
I don't see any need for recursion here, so I'll just make a suggestion for a design approach:
Implement each criterion as a class:
abstract class Criterion {
protected $valid = true;
abstract public function initialize($value);
abstract public function check($value);
public function isValid() {
return $this->valid;
}
}
class Length extends Criterion {
protected $length;
public function initialize($value) {
$this->length = strlen($value);
}
public function check($value) {
if ($this->length != strlen($value)) {
$this->valid = false;
}
}
}
You then make an array of all your criteria:
$criteria = [new Length, ...];
foreach ($criteria as $criterion) {
$criterion->initialize($values[0]);
}
And slowly whittle them down through your values:
foreach ($values as $value) {
foreach ($criteria as $criterion) {
$criterion->check($value);
}
}
$commonCriteria = array_filter($criteria, function (Criterion $criterion) {
return $criterion->isValid();
});

PHP OOP Login system stdClass::$id

When I login with the correct information I get the following message:
Notice: Undefined property: stdClass::$id in /var/www/classes/User.php on line 37
Success Login!
Line 37 in User.php is following
Session::put($this->_sessionName, $this->data()->id);
Also after i sign in and update page and go to /index.php just to try print out the session, I get nothing.
Following command on index.php page
echo Session::get(Config::get('session/session_name'));
If i don't sign in and just go to index.php i get the following message
Notice: Undefined index: user in /var/www/classes/Session.php on line 12
Login.php
<?php
require_once 'core/init.php';
ini_set('display_errors', 1);
error_reporting(~0);
if(Input::exists()) {
if(Token::check(Input::get('token'))) {
$validate = new Validate();
$validation = $validate->check($_POST, array(
'username' => array('required' => true),
'password' => array('required' => true)
));
if($validation->passed()) {
$user = new User();
$login = $user->login(Input::get('username'), Input::get('password'));
if($login) {
echo 'Success Login!';
} else {
echo 'Login failed';
}
} else {
foreach($validation->errors() as $error) {
echo $error;
}
}
}
}
?>
<form action="" method="post">
<div class="field">
<label for="username">Username</label>
<input type="text" name="username" id="username">
</div>
<div class="field">
<label for="password">Password</label>
<input type="password" name="password" id="password">
</div>
<input type="hidden" name="token" value="<?php echo Token::generate(); ?>">
<input type="submit" value="Logga in">
</form>
Session.php
<?php
class Session {
public static function exists($name) {
return (isset($_SESSION[$name])) ? true : false;
}
public static function put($name, $value) {
return $_SESSION[$name] = $value;
}
public static function get($name) {
return $_SESSION[$name];
}
public static function delete($name) {
if(self::exists($name)) {
unset($_SESSION[$name]);
}
}
public static function flash($name, $string = '') {
if(self::exists($name)) {
$session = self::get($name);
self::delete($name);
return $session;
} else {
self::put($name, $string);
}
}
}
User.php
<?php
class User {
private $_db,
$_data,
$_sessionName;
public function __construct($user = null) {
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
}
public function create($fields = array()) {
if(!$this->_db->insert('users', $fields)) {
throw new Exception('Could not create account');
}
}
public function find($user = null) {
if($user) {
$field = (is_numeric($user)) ? 'id' : 'username';
$data = $this->_db->get('users', array($field, '=', $user));
if($data->count()) {
$this->_data = $data->first();
return true;
}
}
return false;
}
public function login($username = null, $password = null) {
$user = $this->find($username);
if($user) {
if($this->data()->password === Hash::make($password, $this->data()->salt)) {
Session::put($this->_sessionName, $this->data()->id);
return true;
}
}
return false;
}
private function data() {
return $this->_data;
}
}
Token.php
<?php
class Token {
public static function generate() {
return Session::put(Config::get('session/token_name'), sha1(uniqid()));
}
public static function check($token) {
$tokenName = Config::get('session/token_name');
if(Session::exists($tokenName) && $token === Session::get($tokenName)) {
Session::delete($tokenName);
return true;
}
return false;
}
}

Accessing $_POST array from objects called on page

I have a page that receives POST data from a form. I also have a form validation class that attempts to access the $_POST array when a FormValidation object is created on the page. However, the FormValidation object does not seem to have access to the $_POST array. Why is this happening, and how can I fix this?
EDIT: Added code :)
This code is from register.php
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
var_dump($_POST);
$errors = validate_form();
if(count($errors) > 0)
{
display($errors);
}
else
{
act();
}
}
else
{
display();
}
function validate_form()
{
$validator = new FormValidation('POST');
$validator->add_field('first_name', 'First Name');
$validator->add_field('last_name', 'Last Name');
$validator->add_field('email_address', 'Email Address');
$validator->add_field('password', 'Password');
$validator->add_field('password_confirmation', 'Password Confirmation');
$validator->add_rule('first_name', 'required');
$validator->add_rule('last_name', 'required');
$validator->add_rule('email_address', 'required');
$validator->add_rule('email_address', 'is_valid_scarsdaleschools_email');
$validator->add_rule('password', 'required');
$validator->add_rule('password_confirmation', 'required');
$validator->add_rule('password_confirmation', 'matches', array('password', 'Password'));
return $validator->validate();
}
The form validation code looks like this
class FormValidation
{
var $fields;
var $rules; //Associative array that maps field_name to array of rules
var $errors;
var $form_method_type;
function FormValidation($form_method_type)
{
$this->form_method_type = strtoupper($form_method_type);
$this->fields = array(); //maps fields to field labels
$this->rules = array();
$this->errors = array();
}
function validate()
{
foreach(array_keys($this->fields) as $field_name)
{
if(!array_key_exists($field_name, $this->rules))
{
continue;
}
foreach(array_keys($this->rules[$field_name]) as $rule_name)
{
call_user_func_array(array($this, $rule_name), $this->rules[$field_name][$rule_name]);
}
}
return $this->errors;
}
function add_field($field_name, $field_label)
{
$this->fields[$field_name] = $field_label;
}
function add_rule($field_name, $rule_name, $parameters=array())
{
if(!isset($this->rules[$field_name]))
{
$this->rules[$field_name] = array();
}
array_unshift($parameters, $field_name, $this->fields[$field_name]);
$this->rules[$field_name][$rule_name] = $parameters;
}
function var_isset($field_name)
{
global ${'$_' . $this->form_method_type};
var_dump(${'$_' . $this->form_method_type}[$field_name]);
return isset(${'$_' . $this->form_method_type}[$field_name]);
}
function get_value($field_name)
{
global ${'$_' . $this->form_method_type};
return ${'$_' . $this->form_method_type}[$field_name];
}
////////////////////////////////////////////////////////////
/////RULES//////////////////////////////////////////////////
////////////////////////////////////////////////////////////
function required($field_name, $field_label)
{
if(!$this->var_isset($field_name))
{
$this->errors[$field_name] = "$field_label is a required field";
}
}
function is_valid_email($field_name, $field_label)
{
if($this->var_isset($field_name))
{
if(!validEmail($this->get_value($field_name)))
{
$this->errors[$field_name] = "$field_label requires a valid email address";
}
}
}
function is_alpha($field_name, $field_label)
{
if($this->var_isset($field_name))
{
if(!ctype_alpha($this->get_value($field_name)))
{
$this->errors[$field_name] = "$field_label requires alphabetical characters only";
}
}
}
function is_alphanumeric($field_name, $field_label)
{
if($this->var_isset($field_name))
{
if(!ctype_alnum($this->get_value($field_name)))
{
$this->errors[$field_name] = "$field_label requires alphanumeric characters only";
}
}
}
function is_integer_number($field_name, $field_label)
{
if($this->var_isset($field_name))
{
if(!is_int($this->get_value($field_name)) || preg_match('[-+]?[0-9]+', $this->get_value($field_name)) == 0)
{
$this->errors[$field_name] = "$field_label must be an integer";
}
}
}
function is_float_number($field_name, $field_label)
{
if($this->var_isset($field_name))
{
if(!is_float($this->get_value($field_name)) || preg_match('[-+]?[0-9]*\.?[0-9]+', $this->get_value($field_name)) == 0)
{
$this->errors[$field_name] = "$field_label must be a number";
}
}
}
function is_valid_scarsdaleschools_email($field_name, $field_label)
{
if($this->var_isset($field_name))
{
if(!validEmail($this->get_value($field_name)))
{
$this->errors[$field_name] = "$field_label requires a valid email address";
}
$email = $this->get_value($field_name);
if(!(endsWith($email, 'scarsdaleschools.org', $case=false) || endsWith($email, 'scarsdaleschools.com', $case=false) || endsWith($email, 'scarsdaleschools.k12.ny.edu', $case=false)))
{
$this->errors[$field_name] = "$field_label requires a Scarsdale Schools email address";
}
}
}
function max_length($field_name, $field_label, $max_length)
{
if($this->var_isset($field_name))
{
if(strlen($this->get_value($field_name)) > $max_length)
{
$this->errors[$field_name] = "$field_label cannot be longer than $max_length characters";
}
}
}
function min_length($field_name, $field_label, $min_length)
{
if($this->var_isset($field_name))
{
if(strlen($this->get_value($field_name)) > $min_length)
{
$this->errors[$field_name] = "$field_label must be at least $min_length characters";
}
}
}
function matches($field_name, $field_label, $match_field_name, $match_field_label)
{
if($this->var_isset($field_name) && !$this->var_isset($match_field_name))
{
$this->errors[$field_name] = "$field_label must match $match_field_label";
}
elseif(!$this->var_isset($field_name) && $this->var_isset($match_field_name))
{
$this->errors[$field_name] = "$field_label must match $match_field_label";
}
if($this->var_isset($field_name) && $this->var_isset($match_field_name))
{
if(strcmp($this->get_value($field_name), $this->get_value($match_field_name)) != 0)
{
$this->errors[$field_name] = "$field_label must match $match_field_label";
}
}
}
}
Thanks for the help!
There is a small error in your code. What you were writing was essentially $$_POST. You'll need to remove the extra dollar sign from the variable name (corrected methods below). In addition, you needn't worry about calling global $_POST; as $_POST is a superglobal, or automatic global.
Update: Retrieving $_POST via ${'_' . $this->form_method_type} does not seem to work. The code I sent you works OUTSIDE of the class, but not inside. I wanted to make sure you understood my findings, and the distinction. So while ${'_' . $formMethodType} works outside of the class, inside you'll have to use something like this:
const TYPE_POST = 'POST';
const TYPE_GET = 'GET';
const TYPE_SESSION = 'SESSION';
const TYPE_SERVER = 'SERVER';
const TYPE_REQUEST = 'REQUEST';
public $fields = array();
public $rules = array();
public $errors = array();
public function __construct($formMethodType)
{
$r = new ReflectionClass($this);
$constants = $r->getConstants();
$formMethodType = strtoupper($formMethodType);
if (!array_key_exists('TYPE_' . $formMethodType, $constants)) {
throw new InvalidArgumentException('Could not find type matching $formMethodType : ' . $formMethodType);
}
$this->form_method_type = $formMethodType;
}
public function var_isset($field_name)
{
$values = $this->get_values();
var_dump($values[$field_name]);
return isset($values[$field_name]);
}
public function get_value($field_name)
{
$values = $this->get_values();
return $values[$field_name];
}
public function get_values()
{
switch ($this->form_method_type) {
case self::TYPE_POST:
$values = $_POST;
break;
case self::TYPE_GET:
$values = $_GET;
break;
case self::TYPE_REQUEST:
$values = $_REQUEST;
break;
case self::TYPE_SERVER:
$values = $_SERVER;
break;
case self::TYPE_SESSION:
$values = $_SESSION;
break;
}
return $values;
}
We haven't yet seen any code posted, but I'll make a recommendation about using dependency injection to get the $_POST data into your form validation class, rather than accessing the superglobal from inside the class.
Since you can't always rely on $_POST being populated correctly when you are testing your code, it is advisable to inject the contents of $_POST as a parameter to the constructor of a class where it will be used. This makes it easier to debug later on.
class MyClass {
// Public property to hold post array data
public $postdata;
// constructor receives $_POST as a parameter
function __construct($param1, $param2, $postdata) {
//
$this->postdata = $postdata;
}
}
// Instantiate the class with $_POST injected
$x = new MyClass($a, $b, $_POST);
ADDENDUM after code posted
I don't see any method in your class that actually calls the get_value() method when validating. It's unclear if you are ever accessing the contents of $_POST in that class at all other than in the var_isset() and get_value() methods.
/**
* This should work for $_REQUEST and $_POST the mysql_real_escape_string
* was added to escape urls. It is best not to allow URLs as parameters.
*/
foreach( $_POST as $key=> $val)
{
${$key} = mysql_real_escape_string($val);
}

wordpress-like model to view api event system (MVC)

Wordpress has a nice api system. I'm struggling in creating an equal flexible one using a more traditional MVC interpretation. A typical view class could be like this:
class View
{
public function set($name, $value)
{
$this->data[$name] = $value
}
public function __get($name) ... you know how it works
}
A use case would be a controller adding an order contain order lines:
$view->add('order', $orderModel);
Then the template file could have something like
foreach ($this->order->getOrderLines() as $orderLine)
{
?><div><?php echo $orderLine->getItemName(); ?><div><?php
}
At least this is what is commonly seen in many PHP MVC frameworks. I'm open to alternative implementations that solve the question:
How to add an event system like wordpress has? Eg a filter OrderLineItemName.
Okay,
I am not 100% exactly what you want to do, but I have an idea.
Maybe you mean something like this:
class View {
public $hooks = array('getsomeVar');
public $hooks_functions = array();
public $attributes = array();
public function __set($k,$v) {
$this->attributes[$k] = $v;
}
public function __get($k) {
if (isset($this->attributes[$k])){
$hooks = $this->get_functions_by_hook('get' . $k);
if (!empty($hooks)){
foreach ($hooks as $klass=>$methods) {
if (class_exists($klass)){
$class = new $klass();
foreach ($methods as $method) {
if (method_exists($class,$method)){
$this->attributes[$k] = $class->$method($this->attributes[$k]);
}
}
}
}
}
return $this->attributes[$k];
} else {
throw new Exception($k . " is not a view variable");
}
}
public function register_filter($name,$class,$method) {
if (in_array($name,$this->hooks)){
$this->hooks_functions[$name][$class][] = $method;
} else {
throw new Exception($name . ' is not a valid hook');
}
}
public function get_functions_by_hook($name) {
if (array_key_exists($name,$this->hooks_functions)){
return $this->hooks_functions[$name];
}
return array();
}
}
class MyPlugin {
public function fix_string($str) {
return str_replace("ruby",'php',$str);
}
}
$v = new View();
$v->someVar = 'ruby is great';
$v->register_filter('getsomeVar','MyPlugin','fix_string');
echo $v->someVar;
or you can use this method, which is more event like. Again sample code, but you should be able to cannabalize it.
class EventDispatcher {
public static $listeners = array();
public static function registerListener(&$instance) {
if (!in_array($isntance,self::$listeners)){
array_push(self::$listeners,$instance);
}
}
public static function dispatchEvent($name,&$value) {
foreach (self::$listeners as $listener) {
if (method_exists($listener,'interests')){
$funcs = $listener->interests();
if (array_key_exists($name,$funcs)){
foreach ($funcs as $f) {
$value = $listener->$f($value);
}
}
}
}
}
}
class Plugin {
public static function registerPlugin($class_name) {
if (class_exists($class_name)){
EventDispatcher::registerListener(new $class_name());
}
}
}
class Model {
public function __construct() {
EventDispatcher::registerListener($this);
}
public function special($value) {
echo "I got called too!\n\n";
return $value;
}
public function interests() {
return array(
"getsomeVar" => "special",
);
}
}
class View {
public $attributes = array();
public function __set($k,$v) {
$this->attributes[$k] = $v;
}
public function __get($k) {
if (isset($this->attributes[$k])){
EventDispatcher::dispatchEvent('get' . $k,$this->attributes[$k]);
return $this->attributes[$k];
} else {
throw new Exception($k . " is not a view variable");
}
}
}
class MyPlugin {
public function fix_string($str) {
return str_replace("ruby",'php',$str);
}
public function interests() {
return array(
"getsomeVar" => "fix_string",
);
}
}
Plugin::registerPlugin('MyPlugin');
$model = new Model();
$v = new View();
$v->someVar = 'ruby is great';
echo $v->someVar;
This is just some sample code, I don't do it this way at all, but it seems like it may be what you are talking about.
Cheers,
Jason
Addendum:
Most of this stuff is about the WP codebase.
WP accesses variables set in the global scope that are modified like so:
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
global $wp_filter, $merged_filters;
$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
unset( $merged_filters[ $tag ] );
return true;
}
It has some other functions like, has_filter etc...
function apply_filters($tag, $value) {
global $wp_filter, $merged_filters, $wp_current_filter;
$args = array();
$wp_current_filter[] = $tag;
// Do 'all' actions first
if ( isset($wp_filter['all']) ) {
$args = func_get_args();
_wp_call_all_hook($args);
}
if ( !isset($wp_filter[$tag]) ) {
array_pop($wp_current_filter);
return $value;
}
// Sort
if ( !isset( $merged_filters[ $tag ] ) ) {
ksort($wp_filter[$tag]);
$merged_filters[ $tag ] = true;
}
reset( $wp_filter[ $tag ] );
if ( empty($args) )
$args = func_get_args();
do {
foreach( (array) current($wp_filter[$tag]) as $the_ )
if ( !is_null($the_['function']) ){
$args[1] = $value;
$value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
}
} while ( next($wp_filter[$tag]) !== false );
array_pop( $wp_current_filter );
return $value;
}
This is not an event driven system, it's a method lookup table which is just a giant hash that looks up the user defined functions to call.
apply_filters, and all plugin like functions are called procedurally as the code is rendered, here is an example
if ( $prefixed ) {
$value = apply_filters("pre_$field", $value);
$value = apply_filters("${field_no_prefix}_save_pre", $value);
} else {
$value = apply_filters("pre_post_$field", $value);
$value = apply_filters("${field}_pre", $value);
}
Or for actions, in an actual template view like so:
<p class="submit"><input type="submit" class="button" name="submit" value="<?php esc_attr_e('Add Category'); ?>" /></p>
<?php do_action('edit_link_category_form', $category); ?>
</form>

Categories