How would one rewrite the following ...
class crunch {
private $funcs = [];
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($data);
}
}
}
$db = 'dbhandle';
$crunch = new crunch();
$crunch->set('myfunction', function($data) {
global $db;
echo 'db = '. $db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd']);
... which correctly outputs ...
db = dbhandle
[123,"asd"]
... to remove the ugly global requirement when using frequently used variables/handles within dynamically added functions?
Normally, I'd define the global on construction as follows, but this understandably fails with the fatal error Uncaught Error: Using $this when not in object context ...
class crunch {
private $db;
private $funcs = [];
public function __construct($db) {
$this->db = $db;
}
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($data);
}
}
}
$db = 'dbhandle';
$crunch = new crunch($db);
$crunch->set('myfunction', function($data) {
echo 'db = '. $this->db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd']);
What's the cleanest way to accomplish the goal?
EDIT: As #Rajdeep points out, I could pass $db within the $crunch->set() function. But I'd like to avoid this, since each dynamic function could reference anywhere from 0-5 of these private variables, and it would be inelegant to have to call all 5 with every $crunch->set().
Instead of creating a private instance variable $db, you could simply pass this variable to the call() method. Your code should be like this:
class crunch {
private $funcs = [];
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false, $db) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($data, $db);
}
}
}
$db = 'dbhandle';
$crunch = new crunch();
$crunch->set('myfunction', function($data, $db){
echo 'db = '. $db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd'], $db);
Output:
db = dbhandle
[123,"asd"]
Update(1):
In case you want to access $db as instance variable only, the solution would be like this:
class crunch {
public $db;
private $funcs = [];
public function __construct($db) {
$this->db = $db;
}
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($this, $data);
}
}
}
$db = 'dbhandle';
$crunch = new crunch($db);
$crunch->set('myfunction', function($crunch, $data) {
echo 'db = '. $crunch->db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd']);
Note that you have to make $db as public member variable, otherwise it would be inaccessible while calling the set() method.
Related
I'm having an issue with the PHP singleton pattern, specifically with regards to implementing a mysqli wrapper.
class DbHandler
{
private $mysqli;
private $query;
private $results = array();
private $numRows = 0;
public static $instance;
public static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new DbHandler;
}
return self::$instance;
}
public function __construct() {
$this->mysqli = new mysqli("127.0.0.1", "root", "", "improved_portal");
if ($this->mysqli->connect_error) {
die($this->mysqli->connect_error);
}
}
public function query($statement) {
if ($this->query = $this->mysqli->query($statement)) {
foreach ($this->query as $value) {
$this->results[] = $value;
}
$this->numRows = $this->query->num_rows;
return $this;
}
}
public function getResults() {
return $this->results;
}
public function getNumRows() {
return $this->numRows;
}
}
When I go to utilise the class in other objects, I seem to have an issue with the results. Instead of creating a new object each time with unique $results, it seems I am creating copies of the initial object. For example...
$object1 = DbHandler::getInstance();
$object1->query("SELECT * FROM table_a")->getResults();
$object2 = DbHandler::getInstance();
$object2->query("SELECT * FROM table_b")->getResults();
$object2 contains results from both queries, which is obviously not what I expect. The query function clearly loops through the results of the second query, and appends these to the $results property of the first object. How should I call a new instance of the DbHandler class so that each object contains unique properties?
First of all - this is not singleton pattern. As your __construct is public I can do this:
$conn1 = new DbHandler();
$conn2 = new DbHandler();
$conn3 = new DbHandler();
To prevent this - __construct must be protected/private.
Second - everytime you call query() from the same object, this function add results to results property. And this results property is used for all queries without clearing. Surely, it will hold all previous values. Function should be rewritten like:
public function query($statement) {
// clear result from previous function call
$this->results = array();
if ($this->query = $this->mysqli->query($statement)) {
foreach ($this->query as $value) {
$this->results[] = $value;
}
$this->numRows = $this->query->num_rows;
return $this;
}
}
I've got a big problem:
I'm writing a new WebApp without a Framework.
I'm using xampp and sublime text.
I dont know how can i solve this problem.
An example how my DB.php written
class DB{
private static $_baglan = null;
private $_pdo,
$_query,
$hatalar = false,
$sonuc,
$_sayac = 0;
public function __construct(){
try{
$this -> _pdo = new PDO('mysql:host=' . Config::getir('mysql/host') . ';dbname=' . Config::getir('mysql/db'), Config::getir('mysql/kullanici_adi'), Config::getir('mysql/sifre') );
// echo 'baglandi';
}catch(PDOException $e){
die($e->getMessage());
}
}
public static function baglan(){
if (!isset(self::$_baglan)) {
self::$_baglan = new DB();
// echo 'baglandi';
}
return self::$_baglan;
}
public static function query($sql, $parametre=array()){
$this->_hatalar = false; // line 32
if ($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if (count($parametre)) {
foreach ($parametre as $param) {
$this->_query->bindValue($x, $parametre);
$x++;
}
}
if ($this->_query->execute()) {
$this->sonuc=$this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_sayac=$this->_query->rowCount();
}else{
$this->_hatalar=true;
}
}
return $this;
}
public function eylem($eylem, $tablo, $where=array()){
if (count($where)===3) {
$operatorler = array('=', '<', '>', '>=', '<=');
$alan = $where[0];
$operator = $where[1];
$deger = $where[2];
if (in_array($operator, $operatorler)) {
$sql = "{$eylem} FROM {$tablo} WHERE {$alan} {$operator} ?";
if (!$this->query($sql, array($deger))->hatalar()) {
return $this;
}
}
}
return false;
}
public function getir($tablo, $where){
return $this->eylem('SELECT *', $tablo, $where);
}
public function sil($tablo, $where){
return $this->eylem('DELETE', $tablo, $where);
}
public function hatalar(){
return $this->hatalar();
}
}
In my index.php I'm loading maybe
require_once 'core/init.php';
// echo Config::getir('mysql/host');
// calismadi $kullanici = DB::baglan() -> query("SELECT kullanici_adi FROM uye WHERE kullanici_adi = ?", array('oguzhan'));
$kullanici = DB::baglan()->getir('uye', array('kullanici_adi', '=', 'oguzhan'));
if ($kullanici->hatalar()) {
echo 'Kullanıcı yok';
}else{
echo 'Var';
}
Why is the error coming?
Your problem can be solved by removing the static keyword from the query method.
You can only use $this on instantiated objects. self or static references the class itself, not any instantiated objects of the class.
class Person{
private $name;
public function __construct(string $name){
$this->name = $name;
}
public function greet(){
echo 'Hello my name is ' . $this->name;
}
}
$person = new Person('Thomas');
$person->greet(); // prints 'Hello my name is Thomas'
$this works in this example because we first instantiated the object using the new keyword. We are also not using the static keyword on the of the methods and variables.
self and static act only on the class itself and not on the instantiated object. The difference between self and static is explained here.
Here is an example of self usage, notice the use of the static keyword on the methods and variables of the class:
class StaticClass{
private static $variable = '';
public static function change(string $variable){
self::$variable = $variable;
}
public static function print(){
echo self::$variable;
}
}
StaticClass::change('Hello');
StaticClass::print(); // print 'Hello'
User::updatemain($set, $where);
This gives Fatal error: Using $this when not in object context
My user class extends from Dbase class and here is user class function:
public static function activate($set, $where) {
return $this->updatemain($set, $where);
here is dbase class (some part of):
private function query($sql = null, $params = null) {
if (!empty($sql)) {
$this->_last_statement = $sql;
if ($this->_db_object == null) {
$this->connect();
}
try {
$statement = $this->_db_object->prepare($sql, $this->_driver_options);
$params = Helper::makeArray($params);
$x = 1;
if (count($params)) {
foreach ($params as $param) {
$statement->bindValue($x, $param);
$x++;
}
}
if (!$statement->execute() || $statement->errorCode() != '0000') {
$error = $statement->errorInfo();
throw new PDOException("Database error {$error[0]} : {$error[2]}, driver error code is {$error[1]}");
exit;
}
//echo $sql;
return $statement;
} catch (PDOException $e) {
echo $this->formatException($e);
exit;
}
}
}
public function updatemain($set, $where) {
return $this->query($sql, $params);
}
this is part of Dbase class
You are calling static method so there is no $this in that context.
If you want to call other static method from given class then use self::method() but if you want to call non-static method you've got problem. First you have to create new object.
When you use static methods, you can't use $this inside
public static function activate($set, $where) {
return self::updatemain($set, $where);
}
Or you have to use singelton design
EDIT
Best solution - rewrite your class to one point access to DB object. And create Model classes to DB access. See my example code below:
core AppCore
<?php
class AppCore
{
public static $config = array();
public static $ormInit = false;
public static function init($config)
{
self::$config = array_merge(self::$config, $config);
}
public static function db($table)
{
// ORM - see http://idiorm.readthedocs.org/en/latest
if (!self::$ormInit) {
ORM::configure(self::$config['db']['connection']);
ORM::configure('username', self::$config['db']['username']);
ORM::configure('password', self::$config['db']['password']);
self::$ormInit = true;
}
return ORM::for_table($table);
}
}
User model
<?php
class UserModel
{
const TABLE = 'user';
public static function findById($u_id)
{
$result = AppCore::db(self::TABLE)->where('u_id', $u_id)->find_one();
return $result ? $result->as_array() : null;
}
}
AppCore init section
AppCore::init(array(
'db' => array(
'connection' => "mysql:dbname={$db};host={$host}",
'username' => $user,
'password' => $pass
),
));
i hope it help make your code better
I took good care to create this class but I am not sure what is wrong with it. The code runs perfectly if I don't have any content inside,
class TemplateOne{
}
But once I run this code it breaks,
<?php
class TemplateOne {
//Properties
protected $_bgColor;
protected $_logoImagePath;
protected $_headerText;
protected $_leftContentHeader;
protected $_rightContentHeader;
protected $_leftContentBody;
protected $_rightContentBody;
protected $_footer;
protected $_mediaIframe;
protected $_mediaHeight = '';
protected $_mediaWidth = '';
//DB communication
public $DB;
//Constructor
public function __construct(){
//Connect database in construct and close connection in destruct
$config = array();
$config['host'] = 'localhost';
$config['user'] = 'root';
$config['pass'] = 'root';
$config['database'] = 'fanpage_application';
$this->DB = new DB($config);
//init variables
populateDataFromDataBase();
}
//Functions
public function populateDataFromDataBase() {
//Get bgcolor
$this->DB->("SELECT backgroundimage FROM template_style_data WHERE styleid='#list_level'");
$data = $this->DB->Get();
foreach($data as $key => $value)
{
echo $value['backgroundimage'];
}
}
//Getters
public function getBgColor()
{
return $this->_bgColor;
}
public function getLogoImagePath()
{
return $this->_logoImagePath;
}
public function getHeaderText()
{
return $this->_headerText;
}
public function getLeftContentHeader()
{
return $this->_leftContentHeader;
}
public function getRightContentHeader()
{
return $this->_rightContentHeader;
}
public function getLeftContentBody()
{
return $this->_leftContentBody;
}
public function getRightContentBody()
{
return $this->_rightContentBody;
}
public function getFooter()
{
return $this->_footer;
}
public function getMediaIframe()
{
return $this->_mediaIframe;
}
//Setters
public function setBgColor($bgColor)
{
$this->_bgColor = $bgColor;
}
public function setLogoImagePath($logoImagePath)
{
$this->_logoImagePath = $logoImagePath;
}
public function setHeaderText($headerText)
{
$this->_headerText = $headerText;
}
public function setLeftContentHeader($leftContentHeader)
{
$this->_leftContentHeader = $leftContentHeader;
}
public function setRightContentHeader($rightContentHeader)
{
$this->_rightContentHeader = $rightContentHeader;
}
public function setLeftContentBody($leftContentHeader)
{
$this->_leftContentBody = $leftContentHeader;
}
public function setRightContentBody($rightContentBody)
{
$this->_rightContentBody = $rightContentBody;
}
public function setFooter($footer)
{
$this->_footer = $footer;
}
public function setMediaIframe($mediaIframe)
{
$this->_mediaIframe = $mediaIframe;
}
}
?>
You are missing $this-> from your call to populateDataFromDataBase.
Where is the DB class coming from? You may have to include the correct class definition file if it is not already.
$this->DB = new DB($config);
The following is not legal syntax. You will need to actually call a function by name.
$this->DB->("SELECT backgroundimage FROM template_style_data WHERE styleid='#list_level'");
Unless you have another function in the global scope named populateDataFromDataBase which is what you want to call, you will need to add $this-> before you try to call it in your constructor.
populateDataFromDataBase();
Here is your error:
$this->DB->("SELECT backgroundimage FROM template_style_data WHERE styleid='#list_level'");
This is not valid syntax, you need to call a method after $this->DB.
As far as I'm concerned there is only one possible answer: check your PHP errors http://php.net/manual/en/function.error-reporting.php
at least (in devevlopment only, don't show errors in production):
ini_set('display_errors', 1);
error_reporting(-1);
public static function assign($name, $value)
{
$this->params[] = array($name => $value);
}
public static function draw()
{
return $this->params;
}
}
<?php
$test = Templater::assign('key', 'value')->draw();
print_r($test);
I need to function "assign" was static, but $params was common for the whole class..
But this code is not working.
Fatal error: Using $this when not in object context
Any ideas?
It sounds like you want $params to be static:
<?php
class Templater
{
static $params = array();
public static function assign($name, $value)
{
self::$params[] = array($name => $value);
}
public static function draw()
{
return self::$params;
}
}
<?php
Templater::assign('key', 'value');
$test = Templater::draw();
print_r($test);
$this keyword refers to the class instance. When you are trying to call it inside a static method no class instance is used. So your assign method cannot be static to interact with $params, that is not static. Make $params static, or assign dynamic (not static).
<?php
class Templater
{
static var $params = array();
public static function assign($name, $value)
{
$this->params[] = array($name => $value);
}
public static dunction draw()
{
return self::params;
}
}
or:
<?php
class Templater
{
var $params = array();
public function assign($name, $value)
{
$this->params[] = array($name => $value);
}
public dunction draw()
{
return $this->params;
}
}
Both will work, but you must to choose the one that is more adequate for your application's design.
Singleton would work nice here
class Templater {
private static $instance = null;
private $params = array();
public function __construct(){
return $this;
}
public static function instance(){
if(is_null(self::$instance)) self::$instance = new self();
return self::$instance;
}
public function assign($name, $value){
$this->params[$name] = $value;
return $this;
}
public function draw(){
return $this->params;
}
}
Usage:
$test = Templater::instance()
->assign('var1', 'value1')
->assign('var2', 'value2')
->draw();
print_r($test);
If you mean $params to be a static field, use:
class Templater {
private static $params = array();
public static function assign($name, $value) {
self::params[] = array($name => $value);
}
public static dunction draw() {
return self::params;
}
}
static functions have no $this context.
By the way, don't use var for declaring instance variables. That's PHP4. Do it the PHP5 way.