Ok, messing about with classes in PHP and can't get it to work the way I'm used to as a C++/Java-guy. In the "_init" function, if I run a query at the "// query works here" line", everythong works, but in the "getUserID" function, all that happens is said warning...
"getUserID" gets called from login.php (they are in the same dir):
login.php
<?php
include_once 'sitehandler.php';
include_once 'dbhandler.php';
session_start();
#TODO: Safer input handling
$t_userName = $_POST["name"];
$t_userId = $_SESSION['handler']['db']->getUserID($t_userName);
if ($t_userId != -1) {
$_SESSION['user']['name'] = $t_userName;
$_SESSION['user']['id'] = $t_userId;
}
//error_log("user: " . $_SESSION['user']['name'] . ", id: ". $_SESSION['user']['id']);
header("Location: " . $_SERVER["HTTP_REFERER"]);
?>
dbhandler.php
<?php
include_once 'handler.php';
class DBHandler extends HandlerAbstract {
private $m_handle;
function __construct() {
parent::__construct();
}
public function test() {
#TODO: isdir liquibase
#TODO: isfile liquibase-195/liquibase + .bat + execrights
$this->m_isTested = true;
}
public function _init() {
if (!$this->isTested()) $this->test();
if (!file_exists('files/data.db')) {
#TODO: How to to if host is Windows based?
exec('./files/liquibase-1.9.5/liquibase --driver=org.sqlite.JDBC --changeLogFile=files/data_db.xml --url=jdbc:sqlite:files/data.db update');
#TODO: quit if not success
}
#TODO: Set with default data
try {
$this->m_handle = new SQLite3('files/data.db');
} catch (Exception $e) {
die("<hr />" . $e->getMessage() . "<hr />");
}
// query works here
$this->m_isSetup = true;
}
public function teardown() {
}
public function getUserID($name) {
// PHP Warning: SQLite3::prepare(): The SQLite3 object has not been correctly initialised in
$t_statement = $this->m_handle->prepare("SELECT id FROM users WHERE name = :name");
$t_statement->bindValue(":name", $name, SQLITE3_TEXT);
$t_result = $t_statement->execute();
//var_dump($this->m_handle);
return ($t_result)? (int)$t_result['id']: -1;
}
}
When you place something in the session and the script ends, PHP goes through each object and calls the __sleep() magic function. Once it resumes the session, __wakeup() is called.
Resources are not stored in sessions. So every time you want to have a resource available to you, you must initialize it on every script run. This can easily be done, in your example, by implementing the __wakeup() magic method, which should call $this->_init();.
Your code could then become:
<?php
include_once 'handler.php';
class DBHandler extends HandlerAbstract { private $m_handle;
function __construct() {
parent::__construct();
}
public function test() {
#TODO: isdir liquibase
#TODO: isfile liquibase-195/liquibase + .bat + execrights
$this->m_isTested = true;
}
public function _init() {
if (!$this->isTested()) $this->test();
if (!file_exists('files/data.db')) {
#TODO: How to to if host is Windows based?
exec('./files/liquibase-1.9.5/liquibase --driver=org.sqlite.JDBC --changeLogFile=files/data_db.xml --url=jdbc:sqlite:files/data.db update');
#TODO: quit if not success
}
#TODO: Set with default data
try {
$this->m_handle = new SQLite3('files/data.db');
} catch (Exception $e) {
die("<hr />" . $e->getMessage() . "<hr />");
}
// query works here
$this->m_isSetup = true;
}
public function teardown() {
}
public function getUserID($name) {
// PHP Warning: SQLite3::prepare(): The SQLite3 object has not been correctly initialised in
$t_statement = $this->m_handle->prepare("SELECT id FROM users WHERE name = :name");
$t_statement->bindValue(":name", $name, SQLITE3_TEXT);
$t_result = $t_statement->execute();
//var_dump($this->m_handle);
return ($t_result)? (int)$t_result['id']: -1;
}
/**
* Start up the SQLite resource once
* the session is started.
*
*/
public function __wakeup() {
$this->init();
}
}
Related
I am working with php-webdrivers by facebook for selenium to implement integration testing for my site. I am using phphunit to run my tests and have wrapped my code inside the phpunitframework_testcase extended class:
class WebTest extends PHPUnit_Framework_TestCase
{
/**
* #var \RemoteWebDriver
*/
protected $driver;
protected $url='http://example.com';
protected $screenShotDirectoryPath = '/screenshots/';
/**
* #BeforeMethod
*/
protected function setUp()
{
try{
$capabilities = array( WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX );
$this->driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities, 5000);
} catch (Exception $e){
echo $e->getMessage();
}
}
/**
* #AfterMethod
*/
public function tearDown()
{
$this->driver->close();
}
public function testImageUpload()
{
$errorSnaps='';
$myBrowserDriver = $this->driver;
//open the url
$myBrowserDriver->get($this->url);
try{
//get email field in login page
$emailField = $myBrowserDriver->findElement(WebDriverBy::id('email'));
//check if the field is displayed
if(!$emailField->isDisplayed()) {
try {
$errorSnaps = $this->takeScreenshot();
$this->errorLogs[] = "The email input Element is not present, a screen-shot of the error has been placed here --> " . $errorSnaps;
} catch (Exception $e){
$this->errorLogs[] = $e->getMessage();
}
}
} catch (NoSuchElementException $nse){
try {
$errorSnaps = $this->TakeScreenshot();
$this->errorLogs[] = "The email field on ".$this->driver->getCurrentURL()." not found , a screen-shot for the error has been placed here -->" . $errorSnaps;
}catch (Exception $e){
$this->errorLogs[]=$e->getMessage();
}
}
}
php-webdrivers documentation recommends this way to initialize the browser drivers
$capabilities=array(
\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX
);
$this->driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities, 5000);
but does not provide a mechanism to init multiple browser drivers to run my tests with only single test file means considering the code above I have to make different copies for all those browsers and with only one line code difference means if i want to run the test above for chrome then i have to change the line from
$capabilities=array(\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX);
to
$capabilities=array(\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::CHROME);
and save that code with rest all the same code as above in a different file and run my test suit. As you can see this is not an optimal way for implementing my tests and for the sake of code re-usability.
I came across 2 options:
Pass argument from the terminal sending the browser name with parameter like phpunit brName=chrome and getting it via $_SERVER['brName']. I would still have to type in each time I want to run tests for any other browser.
I came across the TestDecorator class below on phpunit site which looked like a more conventional way to achieve what I am doing but could not figure out how would I use it to run my tests.
Where should I put my code so that it detects and runs it? Every time I try to run the below sample code it says no tests were executed. If i have sample test function below how could I run it 4 times using the testdecorator as base class?
Sample Test:
public function sampleTest(){
$this->assertTrue(TRUE);
}
Test Decorator class:
require_once 'PHPUnit/Extensions/TestDecorator.php';
class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;
public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test);
if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}
public function count()
{
return $this->timesRepeat * $this->test->count();
}
public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = $this->createResult();
}
for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
$this->test->run($result);
}
return $result;
}
}
I was not going in the right direction to implement this scenario I should not use the testdecorator class but a more good and easy approach would be.
Setting an environment variable via cmd-line/terminal like
export BROWSER_REQUESTED=chrome for (unix) and set BROWSER_REQUESTED=chrome for (windows),or you can create a .sh file with the following code
export BROWSER_REQUESTED=chrome && phpunit and run it via cmd-line.
Create a BrowserFatory class which listens to the environment variable and initiates the drivers for the browser type requested.
create as much .sh files as much browsers you need to run the test for, i have added the code below to be more descriptive.
WebTest.php
class WebTest extends PHPUnit_Framework_TestCase
{
/**
* #var \RemoteWebDriver
*/
protected $driver;
protected $url='http://example.com';
protected $screenShotDirectoryPath = '/screenshots/';
/**
* #BeforeMethod
*/
protected function setUp()
{
try{
$this->driver = BrowserFactory::drivers();
} catch (Exception $e){
echo $e->getMessage();
}
}
}
BrowserFactory.php
class BrowserFactory
{
public static function drivers()
{
switch ($_SERVER['BROWSER_REQUESTED']) {
case 'chrome':
return self::createChrome();
break;
case "ie":
throw new Exception('Not implemented');
break;
case 'firefox':
default:
return self::createFirefox();
break;
}
}
public static function createChrome()
{
putenv("webdriver.chrome.driver=/path/to/chromedriver");
$service = ChromeDriverService::createDefaultService();
$service->start();
return ChromeDriver::start(DesiredCapabilities::chrome(), $service);
}
public static function createFirefox()
{
// these are just constants defined in bootstrap.php
$seleniumUrl = isset($_SERVER['JENKINS_URL']) ? TEST_ENV_SELENIUM_SERVER : LOCAL_ENV_SELENIUM_SERVER;
return RemoteWebDriver::create(
$seleniumUrl, DesiredCapabilities::firefox()
);
}
}
command-line.sh
export BROWSER_REQUESTED='chrome' && phpunit
Can anyone help me with php classes example. I have to make class "information" that has information about users: id, email, password, first name, last name, phone.
Also, class must have a method to print all the user data on the output.
It's really simple skeleton, because you didn't try anything, so just for you to have idea how it works...
class User
{
private $id;
private $email;
// ...
public function __construct($id, $email...)
{
$this->id = $id;
$this->email = $email;
// ...
}
public function printAll()
{
return $this->id . ' ' . $this->email;
}
}
I suggest you reading this: http://codular.com/introducing-php-classes and then come back with any questions.
Here is an example of a PHP class:
class DBIGenerator{
private $table;
private $name;
private $path;
public function __construct($table,$name='default_file.php',
$path='DEFAULTPATH/'){
$this->table=$table;
$this->name=$name;
$this->path=$path;
}
public function generate(){
// build class header
$str='<?php class '.$this->name.'{';
if(!$result=mysql_query('SHOW COLUMNS FROM '.$this->table)){
throw new Exception('Failed to run query');
}
// build data member declaration
if(mysql_num_rows($result)<1){
throw new Exception('Not available columns in table');
}
$methods='';
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
$str.='private $'.$row['Field'].'=\'\';';
$methods.='public function set'.$row['Field'].'($'.$row
['Field'].'){$this->'.$row['Field'].'=$'.$row
['Field'].';}';
$methods.='public function get'.$row['Field'].'(){return
$this->'.$row['Field'].';}';
// store field names in array
$fields[]=$row['Field'];
}
// build empty constructor
$str.='public function __construct(){}';
// build modifiers and accessors
$str.=$methods;
// build load() method
$str.='public function load(){$r=mysql_query("SELECT * FROM
'.$this->table.' WHERE id=\'$this->id\'");';
$str.='return mysql_fetch_array($r,MYSQL_ASSOC);}';
// build submit() method
$str.='public function submit(){mysql_query("INSERT INTO '.$this-
>table.' SET ';
foreach($fields as $field){
$str.=($field!='id')?$field.'=\'$this->'.$field.'\',':'';
}
$str.='");$this->id=mysql_insert_id();';
$str=preg_replace("/,\"/","\"",$str).'}';
// build update() method
$str.='public function update(){mysql_query("UPDATE '.$this-
>table.' SET ';
foreach($fields as $field){
$str.=($field!='id')?$field.'=\'$this->'.$field.'\',':'';
}
$str=preg_replace("/,$/","",$str);
$str.=' WHERE id=\'$this->id\'");}';
// build delete() method
$str.='public function delete(){mysql_query("DELETE FROM '.
$this->table.' WHERE id=\'$this->id\'");}';
$str.='}?>';
// open or create class file
if(!$fp=fopen($this->path.$this->name.'.php','w')){
throw new Exception('Failed to create class file');
}
// lock class file
if(!flock($fp,LOCK_EX)){
throw new Exception('Unable to lock class file');
}
// write class code to file
if(!fwrite($fp,$str)){
throw new Exception('Error writing to class file');
}
flock($fp,LOCK_UN);
fclose($fp);
// delete temporary variables
unset($fp,$str,$row,$fields,$field,$methods);
}
public function getObject(){
// check if class file exists
if(!file_exists($this->path.$this->name.'.php')){
throw new Exception('Failed to include class file');
}
require_once($this->path.$this->name.'.php');
// create data access object
return new $this->name;
}
}
Read more at http://www.devshed.com/c/a/PHP/Building-ObjectOriented-Database-Interfaces-in-PHP-Updating-the-Application-to-PHP-5/1/#Czocu1kMhhuTvg2e.99
Take a look at the snippet below as a basic way to implementing as expressed:
<?php
class information
{
public $id = 1;
public $email = "mail#mail.com";
public $pw = "A2D7DFEA88AC88"; //Don't forget, PW are usually hashed ;)
public function id() {
echo $this->id;
}
public function email() {
echo $this->email;
}
public function pw() {
echo $this->pw;
}
}
$test = new information();
$test->id;
?>
I am trying to write a custom SESSION class for PHP to move sessions into the database and away from the file system.
An error is thrown only in the _read() method when the closure $connect2DB is being called. In all other methods calling $connect2DB does not cause any errors.
Also if I instantiate the class as an object myself all methods including the infamous _read() works as expected. Can you find the error?
I have following code:
class sessionDBHandler {
public function __construct() {
session_set_save_handler(
array($this, "_open"),
array($this, "_close"),
array($this, "_read"),
array($this, "_write"),
array($this, "_destroy"),
array($this, "_gc")
);
register_shutdown_function('session_write_close');
}
public function _open() {
global $sessCon;
global $HOST;
global $USER;
global $PW;
global $DATABASE;
$sessCon=new mysqli($HOST,$USER,$PW,$DATABASE); //mysqli connection
}
public function _close() {
global $sessCon;
$sessCon->close();
}
public function _read($id) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'select `data` from `'.$SESSIONTAB.'` where `ID`=?',array("s",$id)); //<-- error message here
if(!empty($qRes["res"][0]["data"])) return $qRes["res"][0]["data"];
else return '';
}
public function _write($id,$data) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'select `ID` from `'.$SESSIONTAB.'` where `ID`=?',array("s",$id));
$param=array(); //buliding SQL query parameters
$param[0]="sis";
$param[]=$id;
$param[]=time();
$param[]=$data;
if(!empty($qRes["res"][0]["ID"])) {
$param[0].="s";
$param[]=$id;
}
$qRes=$connect2DB($sessCon,(!empty($qRes["res"][0]["ID"]) ? 'update' : 'insert').' `'.$SESSIONTAB.'` set `ID`=?, `access`=?, `data`=?'.(!empty($qRes["res"][0]["ID"]) ? ' where `ID`=?' : ''),$param);
if($qRes["info"]["affectedRows"]>0) return true;
else return '';
}
public function _destroy($id) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'delete from `'.$SESSIONTAB.'` where `ID`=?',array("s",$id));
if($qRes["info"]["affectedRows"]>0) return true;
else return '';
}
public function _gc($max) {
global $sessCon;
global $connect2DB;
global $SESSIONTAB;
$qRes=$connect2DB($sessCon,'delete from `'.$SESSIONTAB.'` where `access`<?',array("i",(time()-$max)));
if($qRes["info"]["affectedRows"]>0) return true;
else return '';
}
}
and I tell PHP where it findes those functions (before calling session_start() here:
new sessionDBHandler();
My problem seems to be the closure I am using as my MySQL abstraction layer. The error I get is:
Fatal error: Function name must be a string in /Users/user/Sites/script.php on line 401
which suggests to me that it somehow cannot find the namespace of $connect2DB.
The thing is though if I call those functions without __constructor and its session_set_save_handler directive, it will work just fine:
$test=new sessionDBHandler();
$test->_open();
echo $test->_read("d6e08112ff6fc8a7eb6e76832327bf81");
$test->_close();
the above works without an error.
Now I am using closures here because I have to keep the footprint of this script very low and unset $connect2DB at the bottom of the script among other reasons so I would love to keep the current abstraction layer.
Any ideas what I can do here?
thanks everyone!
perhaps the $connect2DB is not initialized when sessions is being called?
connnect2DB
/**
* Simple MySQLi abstraction layer
*
* #param resource $mysqli The MySQLi connection link
* #param string $query The MySQL query for prepaired statement
* #param array $v The parameters to replace ? in $query. First element must be the type
* #param integer $o Option for more debug infos [0]=no infos(default) [1]=adding debug infos
*
* #return array [for select]=associative array of table result [for everything else]=associative array with affectedRows,info and insertID
*
* #author Dominik Wilkowski
*/
$connect2DB=function($mysqli,$query,$v=array(),$o=0) {
if($mysqli->connect_errno) {
return array('info'=>array('error'=>'Connect failed: '.$mysqli->connect_error)); //error handling here
exit();
}
if($v && (substr_count($query,"?")!=strlen($v[0]) || strlen($v[0])!=((count($v)-1)>=0 ? (count($v)-1) : 0))) {
return array('info'=>array('error'=>'Placeholders are unequal! placeholders:'.substr_count($query,"?").', replacements:'.strlen($v[0]).', param:'.(count($v)-1).' ('.$v[0].')')); //error handling here...
exit();
}
if($res=$mysqli->prepare($query)) {
//dynamically bind all $v
if($v) {
$values=array($v[0]);
for($i=1; $i<count($v); $i++) {
${'bind'.$i}=$v[$i];
$values[]=&${'bind'.$i};
}
call_user_func_array(array($res,'bind_param'),$values);
}
$res->execute();
//bind all table rows to result
if(strtolower(substr($query,0,6))=="select") {
$field=$fields=$tempRow=array();
$meta=$res->result_metadata();
while($field=$meta->fetch_field()) {
$fieldName=$field->name;
$fields[]=&$tempRow[$fieldName];
}
$meta->free_result();
call_user_func_array(array($res,"bind_result"),$fields);
//return associative array
$results=array();
$i=0;
while($res->fetch()) {
$results["res"][$i]=array();
foreach($tempRow as $k=>$v2) $results["res"][$i][$k] = $v2;
$i++;
}
$res->free_result();
}
else { //return infos about the query
if($mysqli->warning_count) {
if($err=$mysqli->query("SHOW WARNINGS")) {
$row=$err->fetch_row();
$results["info"]["error"].=$row[0].' ('.$row[1].'): '.$row[2];
$err->close();
}
}
$results["info"]["affectedRows"]=$mysqli->affected_rows;
$results["info"]["info"]=$mysqli->info;
$results["info"]["insertID"]=$mysqli->insert_id;
}
$res->close();
}
if($o===1) { //adding debug infos
$q=$query;
for($i=1;$i<count($v);$i++) $q=preg_replace("/\?/",(substr($v[0],($i-1),1)=="s" ? '"' : '').$v[$i].(substr($v[0],($i-1),1)=="s" ? '"' : ''),$q,1);
$results["info"]["query"]=$q;
$results["info"]["param"]=json_encode($v);
}
if(strtolower(substr($query,0,6))=="update" || strtolower(substr($query,0,6))=="delete") { //optimize at update and delete
preg_match_all('/((update|delete) `(.*)` )/i',$query,$tables);
foreach($tables[3] as $t) $mysqli->query('OPTIMIZE TABLE '.$t);
}
return $results;
};
Update #2
Our earlier test suggested that namespace indeed does not seem to be initialized at the time when _read() runs so I wrote a small test case for this to double check.
<?php
$LOGIN='login credentials';
//Closure
$nob=function() {
return 'closure running!<br>';
};
//Session handler
class sessionDBHandler {
public function __construct() {
session_set_save_handler(
array($this, "_open"),
array($this, "_close"),
array($this, "_read"),
array($this, "_write"),
array($this, "_destroy"),
array($this, "_gc")
);
register_shutdown_function('session_write_close');
}
public function _open() {
global $sessCon;
global $LOGIN;
echo '_open running with: '.$LOGIN.'<br>';
$sessCon='connection established!<br>';
}
public function _close() {
global $sessCon;
echo $sessCon='connection closed!<br>';
}
public function _read($id) {
global $sessCon;
global $nob;
echo $nob();
echo 'requested id is: '.$id.'<br>';
echo 'sessCon is: '.$sessCon;
}
public function _write($id,$data) {
global $sessCon;
echo 'requested id is: '.$id.' and data: '.$data.'<br>';
echo 'sessCon is: '.$sessCon;
}
public function _destroy($id) {
global $sessCon;
echo 'requested id is: '.$id.'<br>';
echo 'sessCon is: '.$sessCon;
}
public function _gc($max) {
global $sessCon;
echo 'requested max is: '.$max.'<br>';
echo 'sessCon is: '.$sessCon;
}
}
new sessionDBHandler();
session_start();
?>
Interestingly enough this will run without errors and output this:
_open running with: login credentials
closure running!
requested id is: 746b70a979d9ef8c6686e659707b4b38
sessCon is: connection established!
requested id is: 746b70a979d9ef8c6686e659707b4b38 and data:
sessCon is: connection established!
connection closed!
Now that suggests that there seems to be an error in my particular implementation. Whoever wants to have a stab at this is very welcome to join :)
Update #3
I want to unset my closure after I run my application and had therefore unset($connect2DB); on the very bottom of the script. This however seemed to have caused a "premature" unset somehow. Removing this will remove the error.
However
Is there a way to safely unset my closure after the script has run so it doesn't flow into the main script?
I have a connection class for MySQL that looks like this:
class MySQLConnect
{
private $connection;
private static $instances = 0;
function __construct()
{
if(MySQLConnect::$instances == 0)
{
//Connect to MySQL server
$this->connection = mysql_connect(MySQLConfig::HOST, MySQLConfig::USER, MySQLConfig::PASS)
or die("Error: Unable to connect to the MySQL Server.");
MySQLConnect::$instances = 1;
}
else
{
$msg = "Close the existing instance of the MySQLConnector class.";
die($msg);
}
}
public function singleQuery($query, $databasename)
{
mysql_select_db(MySQLConfig::DB, $this->connection)
or die("Error: Could not select database " . MySQLConfig::DB . " from the server.");
$result = mysql_query($query) or die('Query failed.');
return $result;
}
public function createResultSet($query, $databasename)
{
$rs = new MySQLResultSet($query, MySQLConfig::DB, $this->connection ) ;
return $rs;
}
public function close()
{
MySQLConnect::$instances = 0;
if(isset($this->connection) ) {
mysql_close($this->connection) ;
unset($this->connection) ;
}
}
public function __destruct()
{
$this->close();
}
}
The MySQLResultSet class looks like this:
class MySQLResultSet implements Iterator
{
private $query;
private $databasename;
private $connection;
private $result;
private $currentRow;
private $key = 0;
private $valid;
public function __construct($query, $databasename, $connection)
{
$this->query = $query;
//Select the database
$selectedDatabase = mysql_select_db($databasename, $connection)
or die("Error: Could not select database " . $this->dbname . " from the server.");
$this->result = mysql_query($this->query) or die('Query failed.');
$this->rewind();
}
public function getResult()
{
return $this->result;
}
// public function getRow()
// {
// return mysql_fetch_row($this->result);
// }
public function getNumberRows()
{
return mysql_num_rows($this->result);
}
//current() returns the current row
public function current()
{
return $this->currentRow;
}
//key() returns the current index
public function key()
{
return $this->key;
}
//next() moves forward one index
public function next()
{
if($this->currentRow = mysql_fetch_array($this->result) ) {
$this->valid = true;
$this->key++;
}else{
$this->valid = false;
}
}
//rewind() moves to the starting index
public function rewind()
{
$this->key = 0;
if(mysql_num_rows($this->result) > 0)
{
if(mysql_data_seek($this->result, 0) )
{
$this->valid = true;
$this->key = 0;
$this->currentRow = mysql_fetch_array($this->result);
}
}
else
{
$this->valid = false;
}
}
//valid returns 1 if the current position is a valid array index
//and 0 if it is not valid
public function valid()
{
return $this->valid;
}
}
The following class is an example of how I am accessing the database:
class ImageCount
{
public function getCount()
{
$mysqlConnector = new MySQLConnect();
$query = "SELECT * FROM images;";
$resultSet = $mysqlConnector->createResultSet($query, MySQLConfig::DB);
$mysqlConnector->close();
return $resultSet->getNumberRows();
}
}
I use the ImageCount class like this:
if(!ImageCount::getCount())
{
//Do something
}
Question: Is this an okay way to access the database? Could anybody recommend an alternative method if it is bad?
Thank-you.
Hey Mike, there's nothing wrong with implementing your own classes to handle database connection, what you have so far is fine, however PHP already provides an interface for handling DB connections regardless of the database manager you are connecting to. I'd recommend you to take a look at it http://www.php.net/manual/en/book.pdo.php since it has mostly all the functionality needed for handling queries, statements, resultsets, errors, and so forth.
Cheers,
M.
I'm not sure that having a class called "ImageCount" is really necessary. If you're going to be working with images - I would simply have a class called "Image" with a static function to get the count of all images, and some other functions to deal with images.
Also, if you try to create a new instance when one exists - how about returning the existing instance instead of using die() to stop the program.
I'm looking for something like break for loops.
Here's some example code (using Symfony's lime) where stop() would not let the class continue and I_DONT_WANT_THIS_TO_RUN() would not be executed.
$browser->isStatusCode(200)
->isRequestParameter('module', 'home')
->isRequestParameter('action', 'index')
->click('Register')
->stop()
->I_DONT_WANT_THIS_TO_RUN();
$browser->thenThisRunsOkay();
Calling $this->__deconstruct(); from within stop() doesn't seem to do the trick. Is there a function I can call within stop() that would make that happen?
You could use PHP exceptions:
// This function would of course be declared in the class
function stop() {
throw new Exception('Stopped.');
}
try {
$browser->isStatusCode(200)
->isRequestParameter('module', 'home')
->isRequestParameter('action', 'index')
->click('Register')
->stop()
->I_DONT_WANT_THIS_TO_RUN();
} catch (Exception $e) {
// when stop() throws the exception, control will go on from here.
}
$browser->thenThisRunsOkay();
Just return another class which will return $this for every method called.
Example:
class NoMethods {
public function __call($name, $args)
{
echo __METHOD__ . " called $name with " . count($args) . " arguments.\n";
return $this;
}
}
class Browser {
public function runThis()
{
echo __METHOD__ . "\n";
return $this;
}
public function stop()
{
echo __METHOD__ . "\n";
return new NoMethods();
}
public function dontRunThis()
{
echo __METHOD__ . "\n";
return $this;
}
}
$browser = new Browser();
echo "with stop\n";
$browser->runThis()->stop()->dontRunThis()->dunno('hey');
echo "without stop\n";
$browser->runThis()->dontRunThis();
echo "the end\n";
Will result in:
with stop
Browser::runThis
Browser::stop
NoMethods::__call called dontRunThis with 0 arguments.
NoMethods::__call called dunno with 1 arguments.
without stop
Browser::runThis
Browser::dontRunThis
the end
OIS's answer is really good, though I could see that it might get confusing if the object suddenly changes to something else. That is, you'd expect that at the end of your chain, you'll end up with the same object. To avoid that problem, I'd add a private variable to tell the class whether or not to actually do anything. If the class has been stopped, then every class just returns $this straight away. This gives you the added benefit of being able to restart the execution.
class MyClass {
private $halt;
function __call($func, $args) {
if ($this->halt) {
return $this;
} else {
return $this->$func($args);
}
}
private function isRequestParameter() {
// ...
}
public function stop() {
$this->halt = true;
}
public function start() {
$this->halt = false;
}
}
This could be put into a parent class so you don't have to duplicate this code.