Added Namespace and lost all the PHP OOPs Classes


#1

I am trying to learn the finer points of Classes and have a project that abuses Classes but it does work.

file: index.php

<?php
declare(strict_types=1);
error_reporting(-1);
ini_set('display_errors', 'true');

require 'Class_test.php'; 
$jb = new Class_test;

$message = $jb->getDbConnection(
  $dbHost='XXX', 
  $dbUser='XXX', 
  $dbPwd='XXX', 
  $dBase='XXX')
); 

echo $message; // DB Connection SUCCESS

file: Class_test.php

<?php 
declare(strict_types=1);
error_reporting(-1);
ini_set('display_errors', 'true');

//==============================
Class Class_test
{

private $dbCon;

//====================================================
public function getDbConnection($dbHost, $dbUser, $dbPwd, $dBase)
{ 
  mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

  $result = 'Unable to connect';
  try {
    $this->dbCon = new mysqli($dbHost, $dbUser, $dbPwd, $dBase);
    $result = 'DB Connection SUCCESS';
  } catch (customException $e) {
     // display custom message
     // echo $e->errorMessage();
  }

  return $result;
}//endfunc

}/// endclass

The above script works OK and when I add namespace to both index.php and Class_test,php the Class loads OK but gives the following error:

Fatal error
Uncaught Error: Class ‘controllers\mysqli’ not found in …/controllers/Class_test.php:471

I tried unsuccessfully to use:

  $this->dbCon = new controllers\mysqli($dbHost, $dbUser, $dbPwd, $dBase);

I have managed to get round this problem by:

$this->dbCon = mysqli_connect($dbHost, $dbUser, $dbPwd, $dBase);
   

And later the script failed using:

  $obj = new DateTime( $sDate, new DateTimeZone('Europe/Belfast') );

Fatal error :
Uncaught Error: Class ‘controllers\DateTime’ not found in /…/controllers/Class_Oops.php:471

How can I use PHP Object Oriented Classes instead of the PHP Procedural functions?


#2

When you define a namespace on a class, all classes referenced to from that class will be assumed to live in the same namespace.

So in your case you used the namespace controllers, and you have the line

$this->dbCon = new mysqli($dbHost, $dbUser, $dbPwd, $dBase);

without any additional information PHP assumes the mysqli class lives in you controller namespace. Obviously it doesn’t, so you have to tell PHP where it does live. There are two ways to do this:

  1. Reference the Fully Qualified Class Name (FQCN):
$this->dbCon = new \mysqli($dbHost, $dbUser, $dbPwd, $dBase);

Note the \ in front of mysqli, this tells PHP that the mysqli class lives in the top level namespace.

  1. Import the class
<?php
// ...

namespace controllers;

use mysqli;

class Class_test
{
    // ...

    public function getDbConnection($dbHost, $dbUser, $dbPwd, $dBase)
    {
        // ...

        $this->dbCon = new mysqli($dbHost, $dbUser, $dbPwd, $dBase);

        // etc ...
    }
}

Here the line use mysqli tells PHP that any time the class mysqli is referenced, you mean mysqli from the root namespace. A \ in front of mysqli is implied here.

HTH :slight_smile:


#3

Also, there is no need to put your index.php in a namespace. Generally people keep that in the root namespace.
You would of course then have to reference you Class_test accordingly, either

use controllers\Class_test;

$controller = new Class_test();

or

$controller = new controllers\Class_test();

Lastly, there is no reason to redefine

error_reporting(-1);
ini_set('display_errors', 'true');

and I would recommend against it. Just define it once in your index.php if you really have to (better would be to define it in your php.ini as that’s where those settings belong) and be done with it.


#4

Actually, I think this is an ok thing to do. I typically wrap this around an if statement though. Pretty much my whole approach on this is to only enable it on localhost hence the if statement. On a live server, this bit would be omitted since this really should only be a development syntax and not a production syntax.


#5

Many thanks for the detailed explanation it was very helpful.

I have read quite a bit about namespaces and none were as clear as your explanation.

I briefly tried your suggestions and delighted to say they both worked.

I am still confused how to refactor this current project. It started quite simple and now consists of a Class and Model both about a thousand lines each! Both classes only acting like a glorified include functions file.

The major confusion is using AJAX which I have little experience. I am convinced the AJAX calls should call a pseudo Controller and generated data passed and rendered via a Partial View . Applying this technique would only use a couple of selected Class Methods and be a lot easier to update and maintain.

It is getting late here so I hope to try again tomorrow to simplify the process.


#6

I did modify the php.ini file but an update overwrote the changes :frowning:

I have just remade the changes again!


#7

AJAX as well would need a full controller and a full view. Only that these do not necessarily produce HTML. It’s more common for AJAX to exchange data using the JSON format, but for an MVC(-like) approach it doesn’t matter if your data format is HTML, XML, JSON, or something completely different. It’s the View’s job to transform the model data into the output format.

The only mistake you should not make is to think an AJAX request as the same as a page load, since an AJAX call does not have to return a valid HTML page.


#8

Maybe try setting LOCALHOST because it is quite handy:

<?php

  defined('LOCALHOST') 
  || 
  define('LOCALHOST', 'localhost'===$_SERVER['SERVER_NAME']);
  ini_set('display_errors', LOCALHOST ? 'TRUE' : 'FALSE');


#9

I just don’t get the point of this. You have your local dev server, ini error reporting is turned on. You have your production server, ini error settings are off. Why then do you need to add error settings in each and every script you work on?


#10

Came here to say exactly that.

You may want error reporting turned on in production but log the errors to a file instead of displaying them. Regardless, having this set in multiple locations is a source of confusion.

What happens when one class loads and turns error reporting on but then the next class loads and turns it off? If you start seeing errors in production you don’t know which class has turned error reporting on.

It’s definitely something that should be set in only one place, and that place should really be php.ini


#11

This is true but in the not so ideal world sometimes hosting companies do not allow us to modify php.ini hence the need to do it in the application code. But still of course, in such cases this should be set in one place like the main index.php.


#12

In that case , index.php is the only real choice, however given that VPSs are dirt cheap these days there is very little reason to use shared hosting.


#13

Yes. Which is pretty much the reason why I mentioned about using it. I don’t personally use it like that myself. Since I debug locally, I don’t really have the necessity to use this because I’ve configured my php.ini file to log errors. But what I meant was that sometimes, people may come to me and ask me to write them a piece of code. Since I do not know their installation and configurations for PHP, it would be a wild guess to just add those piece in.


#14

I was most surprised the stylesheet statements were available no doubt because they are in the DOM. It did save a lot of additional scripting.

I managed to create the AJAX View:

Please note this is in the "Make it Work Stage" and requires refinement to the "Make it Better Stage" :slight_smile:

<?php 
  declare(strict_types=1);
  namespace controllers;

  require '../controllers/Class_Db.php';
  $jb = new Class_Db;

  $sTableTop = <<< ____TMP
      <table id="tt1_tbl_rows" class="mga bd1">
      <tr class="bd1 bga">
        <th>Time </th>
        <th>Temperature </th>
        <th>Humidity  </th>
        <th>Battery  </th>
      </tr>  
____TMP;

  $sTableBot = <<< ____TMP
    </table>
    <br>
____TMP;
    
  $aClr   = ['bgd', 'bgs', 'bge'];
  $lastId = '';

  echo '<h2 class="ooo bgf">Sensor Readings:</h2>';
  
  echo '<dl>';
    
    $lastClr  = '$clr';
    $sensorCount = $jb->dbCountSensors();
    for($i2=0; $i2<=$sensorCount; $i2++):
      $oRows  = $jb->dbGetTableRows($i2, $iRows=9);
      $clr    = $aClr[$i2 % 3];

      while( $row = $oRows->fetch_object() ):
        $lastId   = $row->dev_id;
        if($lastClr !== $clr):
          $lastClr = $clr;
          echo $sTableBot; 
          echo '<dt>Sensor ID: &nbsp; ' .$row->dev_id .'</dt>';
          echo $sTableTop; 
        endif;  
        $ttt = $jb->addHour2UKTime((string) $row->tstamp);
  
        $tmp = <<< ______TMP
          <tr class="$clr" style="font-style:italic">
            <td> {$ttt}               </td>
            <td> {$row->temperature}  </td>
            <td> {$row->humidity}     </td>
            <td> {$row->battery}     </td>
          </tr>
______TMP;
        echo $tmp;  
      endwhile;
    endfor; // ($i2=0; $i2<$sensorCount; $i2++):
    echo '</dl>';

AJAX Ouput:


#15

if $row->tstamp were a DateTime object, you could do:

$tz = new DateTimeZone('Europe/London');
$ttt = $jb->tstamp->setTimezone($tz)->format('c'); // or whatever format you need

#16

Many thanks I will try it tomorrow.


#17

I was going to say that as well. I have a VPS for $4.99 US a month.


#18

Unfortunately the format is not a DateTime Object:

The $time variable comes from the IOT Temperature Device which is stored in a JSON structure.

I had quite a bit of trial and error before I decided on adding an additional table value:

// Original JSON FORMAT stored in tblValues -> time
   $row->time     ==> string(30) "2018-11-02T13:15:37.346992742Z"

// Modified to a MySqli TIMESTAMP
   $row->tstamp ==> string(19) "2018-11-02 13:15:37" 

Any suggestions?


#19

Several things, first it depends on what the configured timezone is of the MySQL database (usually the same as the system that it’s running on). If it’s UTC, you can store the timestamps as you do, otherwise you should use DATETIME to avoid potential DST issues (TIMESTAMP converts timestamps into some internal UTC format). You should decide if you want to store local time or UTC in the database.

For storing local time you’d have to convert the timestamps in PHP, which is quite easy.

$datetime = new DateTimeImmutable($json_datetime);
$timezone = new DateTimeZone('Europe/London');
$db_datetime = $datetime->setTimezone($timezone)->format('Y-m-d H:i:s');

Note: most query builders/ORMs support datetime objects as input.

You could do that in MySQL as well, although that requires that timezone support is enabled.


#20

Both the Web Host and MySql Database are in UK and have the default TimeZone set to ‘Europe/London’.

This is the current script I am using now that British Summer Time is over.

// Original JSON FORMAT stored in tblValues -> time
   $row->time ==> string(30) "2018-11-02T13:15:37.346992742Z"

// Conversion from JSON format to TimeStamp
  $tstamp = substr($tstamp, 0, 16);         // 2018-11-02T13:15
  $tstamp = str_replace('T', ' ', $tstamp); // 2018-11-02 13:15

  $iUK  = strtotime($tstamp); // 1539642060

 /* 
   $britishSummerTime=FALSE // Change again 2019-03-31 
   if( $britishSummerTime ):
      $iUK += 60*60; 
   endif;  
 */

// Modified and saved to tblResults->tStamp as MySqli TIMESTAMP
   $row->tstamp ==> string(19) "2018-11-02 13:15:37"