Копиране на клас

dakata__92

Super Moderator
Колеги, имам един PDO клас от фрийма на гатака. Колегата терорист ме подсети за една моя идея но немога да се справя нещо. Искам да препиша класът PDO използващ PDO на MySQLi клас изпозващ MySQLi конекция. Проблема е че желая да се запазят имената на методите за да мога да ги ползвам като имплементиране на интерфейс. Проблема ми, е че немога да се ориентирам и справя с преписването на функциите от PDO на MySQLi.

PHP:
namespace system\DB;

class PDO{
	private $className = null;
	private $opt = array();
	
    private $db = null;    
    private $stmt = null;
    private $params = array();    
    private $sql;
    
	public function __construct($host,$user,$pass,$name){
		$this->className = str_replace(__NAMESPACE__ . "\\", "", __CLASS__);
		$this->opt = $this->options('DB/'.$this->className);
		
		$this->db = new \PDO("mysql:host={$host};dbname={$name}",$user,$pass,$this->opt['pdo']);
	}
	
    private function options($path){
		return \system\core\SAL::getInstance()->MDC()->load($path);
	}
    /**
     * 
     * @param type $sql
     * @param type $params
     * @param type $pdoOptions
     * @return \GF\DB\SimpleDB
     */
    public function prepare($sql, $params = array(), $pdoOptions = array()) {
        $this->stmt = $this->db->prepare($sql, $pdoOptions);
        $this->params = $params;
        $this->sql = $sql;
        return $this;
    }
    
    /**
     * 
     * @param type $params
     * @return \GF\DB\SimpleDB
     */
    public function execute($params = array()) {
        if($params){
            $this->params = $params;
        }        
        $this->stmt->execute($this->params);        
        return $this;
    }    
    
    public function fetchAllAssoc() {
        return $this->stmt->fetchAll(\PDO::FETCH_ASSOC);
    }
    
    public function fetchRowAssoc() {
        return $this->stmt->fetch(\PDO::FETCH_ASSOC);
    }
    
    public function fetchAllNum() {
        return $this->stmt->fetchAll(\PDO::FETCH_NUM);
    }

    public function fetchRowNum() {
        return $this->stmt->fetch(\PDO::FETCH_NUM);
    }

    public function fetchAllObj() {
        return $this->stmt->fetchAll(\PDO::FETCH_OBJ);
    }

    public function fetchRowObj() {
        return $this->stmt->fetch(\PDO::FETCH_OBJ);
    }

    public function fetchAllColumn($column) {
        return $this->stmt->fetchAll(\PDO::FETCH_COLUMN, $column);
    }

    public function fetchRowColumn($column) {
        return $this->stmt->fetch(\PDO::FETCH_BOUND, $column);
    }

    public function fetchAllClass($class) {
        return $this->stmt->fetchAll(\PDO::FETCH_CLASS, $class);
    }

    public function fetchRowClass($class) {
        return $this->stmt->fetch(\PDO::FETCH_BOUND, $class);
    }

    public function getLastInsertId() {
        return $this->db->lastInsertId();
    }

    public function getAffectedRows() {
        return $this->stmt->rowCount();
    }

    public function getSTMT() {
        return $this->stmt;
    }
}
 
Не те разбирам с кои функции не можеш да се справиш? Дай някакъв пример, какво се опитваш да направиш и кое не ти се получава.
 
Така искам да е нещо от този сорт.

PHP:
<?php
namespace system\DB;

class MySQLi{
	private $className = null;
	private $opt = array();
	
    private $db = null;    
    private $STMT = null;
    private $PARAMS = array();    
    private $SQL;
    
	public function __construct($host,$user,$pass,$name){
		$this->className = str_replace(__NAMESPACE__ . "\\", "", __CLASS__);
		$this->opt = $this->options('DB/'.$this->className);
		
		$this->db = new \MySQLi($host},$name},$user,$pass);
	}
	
    private function options($path){
		return \system\core\SAL::getInstance()->MDC()->load($path);
	}
    
    public function prepare($sql, $params = array(), $pdoOptions = array()) {
     //Тук с MySQLi не PDO.
    }
    
    public function execute($params = array()) {
        //Тук с MySQLi не PDO.
    }    
    
    public function fetchAllAssoc() {
        //Тук с MySQLi не PDO.
    }
    
    public function fetchRowAssoc() {
        //Тук с MySQLi не PDO.
    }
    
    public function fetchAllNum() {
       //Тук с MySQLi не PDO.
    }

    public function fetchRowNum() {
        //Тук с MySQLi не PDO.
    }

    public function fetchAllObj() {
        //Тук с MySQLi не PDO.
    }

    public function fetchRowObj() {
        //Тук с MySQLi не PDO.
    }

    public function fetchAllColumn($column) {
        //Тук с MySQLi не PDO.
    }

    public function fetchRowColumn($column) {
        //Тук с MySQLi не PDO.
    }

    public function fetchAllClass($class) {
       //Тук с MySQLi не PDO.
    }

    public function fetchRowClass($class) {
       //Тук с MySQLi не PDO.
    }

    public function getLastInsertId() {
     //Тук с MySQLi не PDO.
    }

    public function getAffectedRows() {
      //Тук с MySQLi не PDO.
    }

    public function getSTMT() {
       //Тук с MySQLi не PDO.
    }
}
 
Така предполагам вече имаш конекция така че нека почнем от prepare. Това го премахваме $pdoOptions защото не знам как би могъл да го ползваш. То е в конфигурационнен файл. После ще ти покажа какво съдържа и ти ще си прецениш. Остана $sql, $params.

Код:
public function prepare($sql, $params = array()) {

     //Тук трябва да вземеш конекцията

     $this->_stmt = $this->_konekciqta->prepare($sql);

     //В тези редове просто присвояваме на едни свойства параметрите и заявката.

    $this->_params = $params;
    $this->_sql = $sql;

    //Тука връщаме this за да имаме чейн реакшън демек всичко да е на един ред например $this->prepare('SELECT * FROM login WHERE first_name=?', array($FirstName))->execute()->fetchAllAssoc(); а параметрите са променливите които подаваме в масива и отговарят на питанките. 

    return $this;

    }


Следва екзекуцията :D пак връщаме this, тука ако има параметри ги присвояваме, ако не изпълняваме без параметри.

Код:
public function execute($params = array()) {
        if ($params) {
            $this->_params = $params;
        }
        $this->_stmt->execute($this->_params);
        return $this;
    }


Пробвай така дали ще ти работи, и после ще видим другите функции.
 
Викам така:

PHP:
$name = "Ilko";
$pass = "123456789";
$sql = $db->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)')->execute(array($name,$pass));

Веднага излезна грешка:
[Warning]: mysqli_stmt::execute() expects exactly 0 parameters, 1 given

eто на този ред $this->STMT->execute($this->PARAMS);

PHP:
<?php
namespace system\DB;

class MySQLi{
	private $db = null;    
    private $STMT = null;
    private $PARAMS = array();    
    private $SQL;
	
	public function __construct($host,$user,$pass,$name){
		$this->db = new \MySQLi($host,$user,$pass,$name);
		if($this->db->connect_errno > 0){
			throw new \Exception('No connection to database!', 500);
		}
	}	

	public function prepare($sql, $params = array()) {
		$this->STMT = $this->db->prepare($sql);
        $this->PARAMS = $params;
        $this->SQL = $sql;
        return $this;s;
    }
	
	public function execute($params = array()) {
        if ($params) {
            $this->PARAMS = $params;
        }
        $this->STMT->execute($this->PARAMS);
        return $this;
    }
	
}
 
Първо виждам една синтактична грешка във функцията prepare return $this;s;


Другото викай така, демек масива ти е в prepare, а не в execute
Код:
$this->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)', array($name,$pass))->execute();

Аз принципно го наследявам класа и за това викам prepare само с this, но ти ако не го наследяваш трябва да минеш по процедурата например

$sql = new system\DB\MySQLi();
$sql-> prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)', array($name,$pass))->execute();
 
teroristd каза:
Първо виждам една синтактична грешка във функцията prepare return $this;s;


Другото викай така, демек масива ти е в prepare, а не в execute
Код:
$this->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)', array($name,$pass))->execute();

Аз принципно го наследявам класа и за това викам prepare само с this, но ти ако не го наследяваш трябва да минеш по процедурата например

$sql = new system\DB\MySQLi();
$sql-> prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)', array($name,$pass))->execute();
Колега, не това е проблема. При MySQLi execute не приема параметър и трябва да ползваме bind_param. Тоест $this->STMT->execute(); . Иначе това което си описал е направено нарочно и реално мога да ги въведа както ти си показал или през excecute. Как да уеднаквя PDO-то с MySQLi execute.
 
Същото е и при PDO, просто вместо заявката да е така примерно

$stmt->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)');
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $pass);
$stmt->execute();

ние вместо bindParam подаваме директно параметрите в масив в prepare

$this->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)', array($name,$pass))->execute();
 
teroristd каза:
Същото е и при PDO, просто вместо заявката да е така примерно

$stmt->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)');
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $pass);
$stmt->execute();

ние вместо bindParam подаваме директно параметрите в масив в prepare

$this->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)', array($name,$pass))->execute();

Подаването е ясно. Правя го по твоят начин и излиза същият ерор заради кода в метода, а не заради това което съм подал.
PHP:
$name = "Ilko";
$pass = "123456789";
$sql = $db->prepare('INSERT INTO `user`(`name`, `pass`) VALUES (?, ?)',array($name,$pass))->execute();

Проблема е ето тук:
PHP:
public function execute($params = array()) {
        if ($params) {
            $this->PARAMS = $params;
        }
        $this->STMT->execute($this->PARAMS);
        return $this;
    }

mysqli_stmt::execute() expects exactly 0 parameters, 1 given
това е ерора тоест $this->STMT->execute($this->PARAMS); не приема параметър и за да се оправи трябва да стане $this->STMT->execute(); и грешката се чисти. Проблема е че трябва да има bind param.
 
Еми направи си една функция подобна на сегашната execute, ако има параметри да ги биндва, а execute само да го връща и ще си викаш така

$db->prepare()->bind()->execute();
 
teroristd каза:
Еми направи си една функция подобна на сегашната execute, ако има параметри да ги биндва, а execute само да го връща и ще си викаш така

$db->prepare()->bind()->execute();
А що просто не промени execute-то така:

PHP:
public function execute($params = array()) {
        if ($params) {
            $this->PARAMS = $params;
        }
        $this->STMT->bind_param($this->PARAMS);
        $this->STMT->execute();
        return $this;
    }

Само че тук има една особеност: ако параметрите са ти примерно
Код:
$params = [1, 'user'];
ще трябва да ги подадеш така:
Код:
$stmt->bind_param('is', 1, 'user');
защото bind_param като първи аргумент приема стринг, в който указваш кой параметър от какъв тип е (очевидно i = int, s = string, вероятно има и други, аз ползвам само тези)
(даже мисля, че горният пример няма да работи, защото bind_param приема references към променливите, а не самите стойности)
Така че тук нещата се усложняват малко.

Другото нещо е, че bind_param приема не array, а поредица от променливи. Затова ще трябва да ползваш нещо такова:

PHP:
call_user_func_array([$stmt, 'bind_param'], $params);
http://php.net/call_user_func_array
По този начин масивът $params ще бъде "екстрактнат" и променливите ще се подадат на $stmt->bind_param() поотделно.
 
Анонимен, наистина много добре си го обяснил схванах, но нещо практически немога да си го представя. Метода който даде дава ерор:
Wrong parameter count for mysqli_stmt::bind_param()
тоест точно това което си обяснил. Идва тънкият момент, който трябва да спомена а именно че се подават параметрите като тези на pdo тоест искам потребителя с промяната само на един стринг да смени от pdo на mqsqli. Нужно ли е да подавам на bind_param стрингове s i и прочие? При PDO това не е наложително и ако реши бързо да премине потребителя от единият тип на другия ще стане мазало ако подавам и тези параметри. http://php.net/call_user_func_array как да я подкарам. Има пример пробвах да я подкарам ето така но пак дава грешка:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
в код

PHP:
	public function execute($params = array()) {
        if($params){
            $this->PARAMS = $params;
        }
		call_user_func_array(array($this->STMT, 'bind_param'), $this->PARAMS);
        $this->STMT->execute();
        return $this;
    }
 
Можеш да минеш през $params по този начин:

PHP:
$first_arg = '';
foreach($params as $val) {
if(is_numeric($val))
   $first_arg .= 'i';
else
   $first_arg .= 's';
}
(предполагам разбираш какво става без обяснения)

А дори още по-лесно ще стане, ако приемеш, че всичко са стрингове. Tук на помощ идваше една функция, май беше нещо като str_repeat('s', count($params)) - трябва да напълни $first_arg с толкова s-та, колкото са елементите в $params.

А за проблема с reference/value - в mysqli наистина е много усукано - google "mysqli bind param pass array" ми дава това, може да ти свърши работа:
http://www.pontikis.net/blog/dynamically-bind_param-array-mysqli
 
anonimen каза:
Можеш да минеш през $params по този начин:

PHP:
$first_arg = '';
foreach($params as $val) {
if(is_numeric($val))
   $first_arg .= 'i';
else
   $first_arg .= 's';
}
(предполагам разбираш какво става без обяснения)

А дори още по-лесно ще стане, ако приемеш, че всичко са стрингове. Tук на помощ идваше една функция, май беше нещо като str_repeat('s', count($params)) - трябва да напълни $first_arg с толкова s-та, колкото са елементите в $params.

А за проблема с reference/value - в mysqli наистина е много усукано - google "mysqli bind param pass array" ми дава това, може да ти свърши работа:
http://www.pontikis.net/blog/dynamically-bind_param-array-mysqli
Това със str_repeat('s', count($params)) как да го съчетая?
PHP:
$s = str_pad('s', count($this->PARAMS));
		
		call_user_func_array(array($this->STMT, 'bind_param'), array_unshift(array($s),$this->PARAMS));
Така получавам грешка.
 
Edit: това казва същото, така че сме на прав път: http://stackoverflow.com/a/16236455

dakata__92 каза:
Пфф... typo или какво? :) array_unshift приема първо масива, после стойността:

PHP:
$s = str_pad('s', count($this->PARAMS);
$params = array_unshift($this->PARAMS, $s);
call_user_func_array(array($this->STMT, 'bind_param'), $params);
И ще продължиш да получаваш грешката за value/reference, защото bind_param приема поинтери към променливите, а не стойности.
Затова ти показах това - http://www.pontikis.net/blog/dynamically-bind_param-array-mysqli - скролваш до the solution, там е описано коя променлива какво означава.

Ей, голяма мъка е тоз mysqli! Но пък е удобно фетчването като ползваш bind_result :)
 
Изчистих грешките но заради референцията се получава нещо странно.
PHP:
public function execute($params = array()) {
        if($params){
            $this->PARAMS = $params;
        }
                print_r($this->PARAMS);
		$_params = array();
		foreach($this->PARAMS as $k => $v){
			$_params[$k] = &$v;
		}
		print_r($_params);
		array_unshift($_params, str_repeat('s', count($_params)));
		call_user_func_array(array($this->STMT, 'bind_param'),$_params);
        $this->STMT->execute();
        return $this;
    }
От двата принта вадя входните и изходните стойности. Реално става това и направо забих, с кое греша в момента.
print_r($this->PARAMS);
Array ( [0] => Ilko [1] => 123456789
)
В червено е грешният изход от принта. Как да го оправя?

print_r($_params);
Array ( [0] => 123456789 [1] => 123456789 )


Променяйки малко метода пак е същият проблема.
PHP:
public function execute($params = array()) {
        if($params){
            $this->PARAMS = $params;
        }
		print_r($this->PARAMS);
		$_params[] = str_repeat('s', count($this->PARAMS));
		foreach($this->PARAMS as $k => $v){
			$_params[] = &$v;
		}
		print_r($_params);
		call_user_func_array(array($this->STMT, 'bind_param'),$_params);
        // $this->STMT->execute();
        return $this;
    }
 
Каква е идеята да присвояваш референция? Нямаш си и на идея за каква мътилка може да се получи, дето няма дебъгване. Имаче пробвай да сложиш едно unset($v); преди foreach($this->PARAMS as $k => $v).

ПП: видях, че се мъчиш да ползваш call_user_func_array(). Навсякъде, където ползваш референция, след това (а за по-сигурно може и преди това) я унищожавай - правилата за локалните промелниви са различни, когато се намесват референции.
 
dakata__92 каза:
Изчистих грешките но заради референцията се получава нещо странно.
От двата принта вадя входните и изходните стойности. Реално става това и направо забих, с кое греша в момента.
print_r($this->PARAMS);
Array ( [0] => Ilko [1] => 123456789
)
В червено е грешният изход от принта. Как да го оправя?

print_r($_params);
Array ( [0] => 123456789 [1] => 123456789 )

Пробвай да обърнеш масива с array_reverse.
 
lamerko каза:
Каква е идеята да присвояваш референция? Нямаш си и на идея за каква мътилка може да се получи, дето няма дебъгване. Имаче пробвай да сложиш едно unset($v); преди foreach($this->PARAMS as $k => $v).

ПП: видях, че се мъчиш да ползваш call_user_func_array(). Навсякъде, където ползваш референция, след това (а за по-сигурно може и преди това) я унищожавай - правилата за локалните промелниви са различни, когато се намесват референции.
Бинд парам изисква референция за да мога да го направя автоматичен. Използвах ънсет още преди ти да пишеш но уви не се получава и ми изтрелваотново грешката:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
 
Оххх ама съм бил заплес бре! Всичко идва заради foreach. Смених го с for и се получи правилно.
PHP:
public function execute($params = array()) {
        if($params){
            $this->PARAMS = $params;
        }
		$_params[] = str_repeat('s', count($this->PARAMS));
		for($i=0;$i < count($this->PARAMS);$i++){
			$_params[] =& $this->PARAMS[$i];
		}
		call_user_func_array(array($this->STMT, 'bind_param'),$_params);
        $this->STMT->execute();
        return $this;
    }
Сега другите как да ги подкарам?
 

Back
Горе