I have a problem with the accessing of classes.
index.php
include('includes/header.php');
include('includes/step1.php');
include('includes/footer.php');
header.php
session_start();
include('php/classes/Errorhandler.php');
include('php/classes/User.php');
$errorhandler = new Errorhandler();
$user = new User();
// Test
print_r($errorhandler->errors);
...html
Errorhandler.php
class Errorhandler {
public $errors = array();
...
}
User.php
class User {
public function __construct() {
if($this->grab_computerid()) {
if(!$this->grab_mandant()) {
$errorhandler->errors[] = "102: There was an error.";
}
if(!$this->grab_os()) {
$errorhandler->errors[] = "103: There was an error.";
}
} else {
$errorhandler->errors[] = "101: There was an error.";
}
}
private function grab_computerid() {
$sqlconnection = new SqlConnection();
$conn = $sqlconnection->db_connect();
if ($conn) {
$query = "SELECT Computer_Idn FROM " . DB_PC_TABLE . " WHERE DeviceName = ?";
$params = array($this->get_hostname());
if ($sqlconnection->query($query, $params)) {
$computer_id = $sqlconnection->fetchRow();
$this->set_computer_id($computer_id['Computer_Idn']);
return true;
echo "Test";
} else {
$errorhandler->errors[] = "Statement error occurred.";
}
} else {
$errorhandler->errors[] = "Can't connect to database.";
// test
print_r($errorhandler->errors);
}
$sqlconnection->db_disconnect();
}
}
The index.php includes the relevant sections to build the site. In header.php I create two objects (1. errorhandler, 2. user). The User class check the return (boolean) from the sqlconnection. I know, that I use the wrong password and get a false. So the if ($conn) prints the print_r($errorhandler->errors) correctly. But when I want to show the errors in step1.php, the array errors is empty.
step1.php
// show negative messages
if ($errorhandler->errors) {
foreach ($errorhandler->errors as $error) {
echo '<div class="alert alert-danger message"><strong>Error: </strong>' . $error . '</div>';
}
}
I tested it in header.php also and the errors array is empty too. So, the errors array is only filled in the User.php class, but I want to display the erros in step1.php. Is there a problem with the includes?
Edit:
Hope to make it clearer:
header.php
// load the class Errorhandler
require_once('php/classes/Errorhandler.php');
// load the class User
require_once('php/classes/User.php');
// create errorhandler object
$errorhandler = new Errorhandler();
// create user object
$user = new User();
print_r($errorhandler->errors);
I set an error in class User:
$errorhandler->errors[] = "Can't connect to database.";
The array $errorhandler->errors is empty in header.php.
You have to call a function. You cannot get value directly.
1st->$errorhandler = new Errorhandler();
2nd->Errorhandler.php
class Errorhandler {
function error() //create a function like this
{
$error= /*any error*/;
return $error;
}
}
3rd->
if ($err=$errorhandler->error()) {
foreach ($err as $error) {
echo '<div class="alert alert-danger message"><strong>Error:</strong>'.$error.'</div>';
}
}
Also try using require_once() instead of include();
Try using require_once instead of include. require_once will throw an error when something goes wrong and kill the script. These errors should appear in an error_log file in the same folder as the file which has the include in it. include will only issue a warning and let the script continue. I personally use require_once everywhere. Also, double check your include paths.
You should pass $errorhandler to User class.
Like this:
$user = new User($errorhandler);
And in User class:
class User {
protected $err;
public function __construct($errorhandler) {
$this->err = $errorhandler;
//rest of your code
}
//rest of your code
}
or simply add: global $errorhandler; inside User constructor.
Related
I'm dealing with a PHP application with what seems to have a peculiarity: One of its files (helpers.php) has a couple of functions that includes another file, and the included file (db_connection.php) includes the file that originally included it.
helpers.php:
<?php
function lineBreak()
{
return "\n<br>\n";
}
function saveScoreToDB($score)
{
//session_start(); // Already started
$usuario_id = $_SESSION["usuario_id"];
$etapa = $_SESSION["etapa"];
try
{
$query_etapa = "SELECT id FROM etapas WHERE numero = $etapa";
require_once "db_connection.php";
// `$db_link` works perfectly fine here:
$etapa_id = $db_link->query($query_etapa)->fetchColumn();
$query_score = "INSERT INTO score
(
usuario_id,
etapa_id,
pontos
)
VALUES
(
$usuario_id,
$etapa_id,
$score
)";
$db_link->query($query_score);
}
catch (Exception $e)
{
$_SESSION["error_message"] = $e->getMessage();
header("Location: erro.php");
}
}
function completeTest($redirectTo)
{
unset($_SESSION["etapa"]);
$usuarioId = $_SESSION["usuario_id"];
// TODO: try/catch
try
{
$queryEmailUsuario = "SELECT email FROM usuarios WHERE id = $usuarioId";
$queryNomeUsuario = "SELECT nome FROM usuarios WHERE id = $usuarioId";
require_once "db_connection.php";
// `$db_link` does *not* work here. Why?
$emailUsuario = $db_link->query($queryEmailUsuario)->fetchColumn();
$nomeUsuario = $db_link->query($queryNomeUsuario)->fetchColumn();
// Routine to send email using the variables above
}
catch (Exception $ex)
{
// TODO
}
}
db_connection.php:
<?php
require_once "db_credentials.php";
require_once "helpers.php";
// Variables used here come from `db_credentials.php`
$dsn = "mysql:host=$host;dbname=$dbname;port=3307;charset=utf8;";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];
try
{
$db_link = new PDO($dsn, $user, $pass, $options);
}
catch (PDOException $e)
{
echo "Error connecting to the database.";
echo lineBreak();
echo $e->getMessage();
echo lineBreak();
echo lineBreak();
}
Notice how in the first script variable $db_link is used in two different functions, both of which include the file where this variable is defined. Within the first function (saveScoreToDB), the variable is available and the function works fine; but within the second (completeTest) it is not available and I get an undefined variable error.
Why is that? How to make it work?
The first require_once() works because that's the "once", but it's only in-scope in that single function call, so $db_link gets tossed out at the end of the function call and is never seen again. You can change that to require(), but creating a new connection for every single function call is... not going to work out well in the long run.
Ideally you create the connection once and then pass it in via parameters where it is needed, eg:
require_once('db_credentials.php');
saveScoreToDB($score, $db_link);
completeTest($redirectTo, $db_link)
But that might get a bit tedious, right? Well this is where classes become useful.
class MyThing {
protected $db;
public function __construct(\PDO $db) {
$this->db = $db;
}
public function saveScoreToDB($score) {
$this->db->prepare(...);
}
public function completeTest($redirectTo) {
$this->db->prepare(...);
}
}
$thing = new Mything($db_link);
$thing->saveScoreToDB(42);
$thing->completeTest('yes');
I'm coming from Java programming and I'm trying to apply my knowledge in OOP style programming in PHP.
So, I tried to create a utility class to connect to database just like how I usually do it in Java where I create a static method to get the database connection.
However, after spending hours I still can't fix the error.
DBHelper.php
<?php
class DBHelper
{
protected $db_name = 'myDb';
protected $db_user = 'root';
protected $db_pass = '';
protected $db_host = 'localhost';
public function obtainConnection()
{
$mysqli_instance = new mysqli($this->db_host, $this->db_user, $this->db_pass, $this->db_name);
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
return $mysqli_instance;
}
}
?>
There are no errors in this file
Then I tried to use it on another file called login.php
login.php
<?php
if (isset($_POST['submit'])) {
include "/DBUtility/DBHelper.php";
$username = $_POST['username']; //s means string
$password = $_POST['password']; // s means string
echo "<br/> Username value: " . $username;
echo "<br />Password value: " . $password;
}
if (empty($username) || empty($password) ) {
echo "Fill out the fields!";
} else {
//PREPARE THE PreparedStatment or Stored Procedure
$dbHelper = new DBHelper();
$connection = $dbHelper->obtainConnection();
$preparedStatement = $connection->prepare('CALL getUserRoleByLogin(?, ?)'); //getUserRoleByLogin() is the name of stored proc in mysql db
$preparedStatement->bind_param('ss', $username, $password); //assign arguments to ? ?
$preparedStatement->execute();//execute the stored procedure. This will return a result
$userRole = $preparedStatement->store_result();
$countOfRows = $preparedStatement->num_rows;
?>
I read every related question about the Fatal error: Cannot redeclare class CLASSNAME error. I tried following the instructions given by many which is to use require_once("DBHelper.php"); instead of include("DBHelper.php");
but still can't get rid of the error.
I tried making the obtainConnection() static and called it via DBHelper::obtainConnection(); but with no luck. Same error message.
I get the error on opening brace of class DBHelper {
I hope you can help me with this.
Thank you.
A couple tips you should do when doing OOP in PHP:
1) I would maybe rethink about not baking the db credentials into your class directly, it makes it harder/more cumbersome to modify them via UI if you wanted to implement a UI control mechanism down the line. Instead, try making a define or maybe a json pref file or a dynamically-created php file that contains an array, something like that. I will do a define because it's the easiest to demonstrate:
/config.php
# You can create a series of defines including the database
define('DB_HOST','localhost');
define('DB_NAME','dbname');
define('DB_USER','root');
define('DB_PASS','dbpassword');
# To maximize compatibility it's helpful to define fwd/back slash
define('DS',DIRECTORY_SEPARATOR);
# It is helpful to create path defines for easy file inclusion
define('ROOT_DIR',__DIR__);
define('CLASSES',ROOT_DIR.DS.'classes');
# Start session
session_start();
2) Create a class autoloader in the config.php file which then allows you to not have to manually include/require classes in pages. It will automatically include them:
spl_autoload_register(function($class) {
if(class_exists($class))
return;
# This will turn a namespace/class into a path so should turn:
# $db = new \DBUtility\DBHelper();
# into:
# /var/www/domain/httpdocs/classes/DBUtility/DBHelper.php
$path = str_replace(DS.DS,DS,CLASSES.DS.str_replace('\\',DS,$class).'.php');
# If the class file is located in the class folder, it will include it
if(is_file($path))
include_once($path);
});
3) I am going to create a static connection so you don't create a new connection every time (also I will use PDO):
/classes/DBUtility/DBHelper.php
<?php
namespace DBUtility;
class DBHelper
{
protected $query;
private static $con;
public function connection()
{
# This will send back the connection without making a new one
if(self::$con instanceof \PDO)
return self::$con;
# I like to catch any pdo exceptions on connection, just incase.
try {
# Assign the connection
self::$con = new \PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
}
catch(\PDOException $e) {
# Here you can just die with a more user-friendly error.
# It would be helpful to save the actual error to a log file
$msg = $e->getMessage();
# I would put your log outside the root or in a protected folder
$txt = realpath(ROOT_DIR.DS.'..').DS.'errors'.DS.'sql.txt';
# Make a directory if none set
if(!is_dir(pathinfo($txt,PATHINFO_DIRNAME))) {
# Make the directory
if(mkdir(pathinfo($txt,PATHINFO_DIRNAME),0744,true)) {
# Save to log file
file_put_contents($txt,$msg.PHP_EOL);
}
}
else {
# Save to log file
file_put_contents($txt,$msg.PHP_EOL);
}
die("Site is under maintenance.");
}
}
# It would be helpful to create a query that will bind and not bind
public function query($sql,$bind = false)
{
if(is_array($bind)) {
foreach($bind as $key => $value) {
$sKey = ":{$key}";
$bindArr[$sKey] = $value;
}
$this->query = $this->connection()->prepare($sql);
$this->query->execute($bindArr);
}
else {
# The second "query" on this is the method from PDO, not the
# "query" method from this class
$this->query = $this->connection()->query($sql);
}
return $this;
}
public function getResults()
{
if(empty($this->query))
return false;
while($result = $this->query->fetch(\PDO::FETCH_ASSOC)) {
$row[] = $result;
}
return (isset($row))? $row : false;
}
}
# If your page ends with a php tag, you should just remove it. It will
# protect against empty spaces that may cause "header already sent" errors
3a) I use something similar to this to autoload functions:
/classes/Helper.php
class Helper
{
public static function autoload($function)
{
if(function_exists($function))
return;
$path = ROOT_DIR.DS.'functions'.DS.$function.'.php';
if(is_file($path))
include_once($path);
}
}
4) Create useful/reusable functions or class/methods
/functions/getUserRole.php
function getUserRole($username,$password,\DBUtility\DBHelper $DBHelper)
{
return $DBHelper->query('CALL getUserRoleByLogin(:0, :1)',array($username,$password))->getResults();
}
/index.php
# Include the config file
require_once(__DIR__.DIRECTORY_SEPARATOR.'config.php');
if (isset($_POST['submit'])) {
# No need for this line ->> include "/DBUtility/DBHelper.php";
# Use trim to remove empty spaces on the left and right
$username = trim($_POST['username']);
$password = trim($_POST['password']);
}
if (empty($username) || empty($password) ) {
echo "Fill out the fields!";
} else {
# User our function autoloader to include this function
Helper::autoload('getUserRole');
# Use the function and inject the DB class
$userRoles = getUserRole($username,$password,new \DBUtility\DBHelper());
$count = count($userRoles);
echo "Count: {$count}";
echo '<pre>';
print_r($userRoles);
echo '</pre>';
}
I am testing an Custom Exception class LoggedException responsible for logging the message.
But its not working as expected, my directory structure for the log file is logs/exceptions.log
I have implemented certain checks for the existence of file and even the check for the implementation of error_log(), which tells the file is written but when I open exceptions.log there is nothing in there and the message I want to write is which thrown.
class LoggedException extends exception{
public function __construct($message,$code=0,$file='logs/exceptions.log')
{
if(file_exists($file)){
if(error_log($this->getMessage(),0,$file)){
echo "<br/>File Wirtten error message: ".$this->getMessage();
} else {
echo"<br/>cannot write";
}
} else {
echo "</br>No such file there";
}
}
}
class someClass{
private $prop="on";
public function checkState($device){
if(($this->prop)=="on"){
throw new LoggedException("The $device is ON");
}
else{
echo ("THE $device IS OFF");
}
}
}
$bulb=new SomeClass();
try{
$bulb->checkState("bulb");
}
catch(LoggedException $e){
echo "Exception message: ".$e->getMessage();
}
Browser Display:
exceptions.log:(also not complete)
Check the manual for the proper way to extend a class Extending Exceptions
First:
You are missing a parameter on your class constructor
public function __construct($message,$code=0,$file='logs/exceptions.log')
The third parameter should be Exception $previous = null like this:-
public function __construct($message, $code = 0,
Exception $previous = null,
$file='./logs/exceptions.log')
Second:
you do not call parent::__construct($message, $code, $previous); before doing your own code in the __construct method of your class.
So the Exception class is not initialised properly, but you attempt to use its properties, which of course are not yet loaded.
Third:
with the option set to 0 in the error_log() function call, the message will go to the standard PHP error log and not the one you intended.
So change 0 to 3 and it will go to the file you specify in param 3.
Fourth:
You need to add your own Newlines manually to the messages written to the log.
Change your code to
class LoggedException extends Exception{
public function __construct($message, $code = 0, Exception $previous = null, $file='./logs/exceptions.log')
{
parent::__construct($message, $code, $previous);
if(file_exists($file)){
if(error_log($this->getMessage() . PHP_EOL, 3, $file)){
echo "<br/>File Wirtten error message: ".$this->getMessage().PHP_EOL;
} else {
echo"<br/>cannot write";
}
} else {
echo "</br>No such file there";
}
}
}
And with any luck it will do roughly what you intended.
session.php
include("database.php");
function addPOTW($subweek, $subtitle, $subcaption, $subsubmittedby)
{
global $database, $form;
/* Errors exist, have user correct them */
if ($form->num_errors > 0) {
return 1; // Errors with form
}
/* No errors, add the new POTW to the database */
else {
if ($database->addNewPOTW($subweek, $subtitle, $subcaption, $subsubmittedby, $subfile)) {
return 0; //Event signup added succesfully
} else {
return 2; //Event signup attempt failed
}
}
}
This is my function, "addPOTW", located in the file session.php (with useless parts redacted). For some reason, I keep getting the error message: "Fatal error: Call to undefined method MySQLDB::addNewPOTW()" even though it's defined right here:
database.php
class MYSQLDB {
function addNewPOTW($date, $title, $caption, $submitter, $filepath)
{
$q = "INSERT INTO `" . TBL_POTW . "` VALUES ('','$date','$title','$caption','$submitter','$filepath')";
return mysql_query($q, $this->connection);
}
}
I have other functions in session.php accessing functions in database.php using the $database variable in the exact same way, and they work perfectly fine. Any ideas why only this one function is being reported as undefined??
Because you are referring to $this, you would need to instantiate an object from that class and then call the method.
Something like this should get it working...
$database = new MYSQLDB;
Make sure you have it in scope before your addPOTW() function.
I'm playing around with SOAP and PHP. But I can't figure it out why the returned object seems not to keep the instance. Let me show you first the example code and then I will explain:
client.php :
$client = new SoapClient("http://myhost/remote.wsdl");
try {
if($client->login("root","toor")) {
echo $client->getTotal()."\n";
}
} catch (SoapFault $exception) {
echo $exception;
}
server.php :
class Remote {
private $auth = false;
public function login($user, $pass) {
if($user == "root" && $pass == "toor") {
$this->auth = true;
return true;
} else throw new SoapFault("Server","Access Denied to '$user'.");
}
public function getTotal() {
if($this->auth) {
return rand(1000,9999);
} else throw new SoapFault("Server","Error: Not Authorized.");
}
}
$server = new SoapServer("remote.wsdl");
$server->setClass("Remote");
$server->handle();
I'm able to "login" so the returned value from $client->login is true.
But, when I call $client->getTotal, $this->auth is false (and thus the error is raised).
What do I need to do in order to keep the value I set previously?
Thank you in advance...
Ok! the solution was here:
http://www.php.net/manual/en/soapserver.setpersistence.php
This is the result:
session_start(); //Important
$server = new SoapServer("remote.wsdl");
$server->setClass("Remote");
$server->setPersistence(SOAP_PERSISTENCE_SESSION);
$server->handle();
And that's it!