Background:
I have installed the PHP Memcached extension on my live server.
Despite various efforts, I can't seem to install Memcached within my XAMPP development box, so I am relying on the following code to only instantiate Memcached only on the Live server:
My connect file which is included in every page:
// MySQL connection here
// Memcached
if($_SERVER['HTTP_HOST'] != 'test.mytestserver') {
$memcache = new Memcached();
$memcache->addServer('localhost', 11211);
}
At the moment I am instantiating each method, and I can't help thinking that that there is a better way to acheive my objective and wonder if anyone has any ideas?
My class file:
class instrument_info {
// Mysqli connection
function __construct($link) {
$this->link = $link;
}
function execute_query($query, $server) {
$memcache = new Memcached();
$memcache->addServer('localhost', 11211);
$result = mysqli_query($this->link, $query) or die(mysqli_error($link));
$row = mysqli_fetch_array($result);
if($server == 'live')
$memcache->set($key, $row, 86400);
} // Close function
function check_something() {
$memcache = new Memcached();
$memcache->addServer('localhost', 11211);
$query = "SELECT something from somewhere";
if($_SERVER['HTTP_HOST'] != 'test.mytestserver') { // Live server
$key = md5($query);
$get_result = $memcache->get($key);
if($get_result) {
$row = $memcache->get($key);
} else {
$this->execute_query($query, 'live');
}
} else { // Test Server
$this->execute_query($query, 'prod');
}
} // Close function
} // Close Class
I would suggest that you read up on interface-based programming and dependency injection. Here's some example code that might give you an idea about how you should go about it.
interface CacheInterface {
function set($name, $val, $ttl);
function get($name);
}
class MemCacheImpl implements CacheInterface {
/* todo: implement interface */
}
class OtherCacheImpl implements CacheInterface {
/* todo: implement interface */
}
class InstrumentInfo {
private $cache;
private $link;
function __construct($link, $cache) {
$this->link = $link;
$this->cache = $cache;
}
function someFunc() {
$content = $this->cache->get('some-id');
if( !$content ) {
// collect content somehow
$this->cache->set('some-id', $content, 3600);
}
return $content
}
}
define('IS_PRODUCTION_ENV', $_SERVER['HTTP_HOST'] == 'www.my-real-website.com');
if( IS_PRODUCTION_ENV ) {
$cache = new MemCacheImpl();
} else {
$cache = new OtherCacheImpl();
}
$instrumentInfo = new InstrumentInfo($link, $cache);
BTW. You actually have the same problem when it comes to mysqli_query, your'e making your code dependent on a Mysql database and the mysqli extension. All calls to mysqli_query should also be moved out to its own class, representing the database layer.
Related
I think I've a problem in understanding how OOP works. I already changed the code that it works, but it isn't the propper way I think. Following scenario (No, I'm not creating a userlogin by myself, its really just for local dev. to understand OOP better):
I've a database.php file:
class Database {
/* Properties */
private $conn;
private $dsn = 'mysql:dbname=test;host=127.0.0.1';
private $user = 'root';
private $password = '';
/* Creates database connection */
public function __construct() {
try {
$this->conn = new PDO($this->dsn, $this->user, $this->password);
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "";
die();
}
return $this->conn;
}
}
So in this class I'm creating a database connection and I return the connection (object?)
Then I have a second class, the famous User class (actually I'm not using autoload, but I know about it):
include "database.php";
class User {
/* Properties */
private $conn;
/* Get database access */
public function __construct() {
$this->conn = new Database();
}
/* Login a user */
public function login() {
$stmt = $this->conn->prepare("SELECT username, usermail FROM user");
if($stmt->execute()) {
while($rows = $stmt->fetch()) {
$fetch[] = $rows;
}
return $fetch;
}
else {
return false;
}
}
}
So thatare my two classes. Nothing big, as you see. Now, don't get confued about the function name login - Actually I just try to select some usernames and usermails from database and displaying them. I try to achieve this by:
$user = new User();
$list = $user->login();
foreach($list as $test) {
echo $test["username"];
}
And here comes the problem. When I execute this code, I get the following error message:
Uncaught Error: Call to undefined method Database::prepare()
And I'm not sure that I really understand what causes this error.
The code works well when I change the following things:
Change $conn in database.php to public instead of private (I think thats bad...? But when its private, I can only execute querys inside of the Database class, I'm right? So should I put all these querys in the Database class? I think that's bad, because in a big project it will get become really big..)
And the second change I've to do is:
Change $this->conn->prepare to $this->conn->conn->prepare in the user.php file. And here I've really no Idea why.
I mean, in the constructor of the user.php I've a $this->conn = new Database() and since new Database will return me the connection object from DB class, I really don't know why there have to be a second conn->
Do not create classes such as your Database class as it's rather useless. It would make sense to create a database wrapper if it adds some extra functionality to PDO. But given its current code, better to use vanilla PDO instead.
Create a single $db instance from either vanilla PDO or your database class.
Pass it as a constructor parameter into every class that needs a database connection
database.php:
<?php
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new \PDO($dsn, $user, $pass, $opt);
user.php
<?php
class User {
/* Properties */
private $conn;
/* Get database access */
public function __construct(\PDO $pdo) {
$this->conn = $pdo;
}
/* List all users */
public function getUsers() {
return $this->conn->query("SELECT username, usermail FROM user")->fetchAll();
}
}
app.php
include 'database.php';
$user = new User($pdo);
$list = $user->getUsers();
foreach($list as $test) {
echo $test["username"],"\n";
}
output:
username_foo
username_bar
username_baz
Check out my (The only proper) PDO tutorial for more PDO details.
I am very new to TDD. I am using phpunit 7.4x-dev. I have the following abstract class that I am trying to develop unit tests for.
use PDO;
abstract class Model {
protected static function getDB() {
static $db = null;
if ($db === null) {
$db = new PDO(ConfigDatabase::DSN, ConfigDatabase::USER, ConfigDatabase::PASSWORD);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $db;
}
}
I have created the following test to get around the need to deal with the static protected method. And it works if I provide "ConfigureDatabase" class.
use PHPUnit\Framework\TestCase;
class ModelTest extends TestCase {
function newMockClass(){
$stub = new class() extends Model{
function getStaticMethod($methodName){
return self::$methodName();
}
};
return $stub;
}
public function testDatabaseExists() {
$stub = $this->newMockClass();
$db = $stub->getStaticMethod('getDB');
$this->assertInstanceOf(PDO::class,$db);
}
}
Since I do not want my tests to rely on any actual database, How would I fake the calls to PDO.
Following Dormilich suggestion I developed a database interface, just in case I decide later I do not want to use PDO.
interface CRUDImp {
function __construct($datbaseBridgeLikePDO);
...
}
Next I wrote my tests for the constructor. I used setup to make sure I was starting with a fresh mock of \PDO.
class PDOWrapperTest extends TestCase {
private $pdoMock;
private $db;
function setup() {
$this->pdoMock = $this->createMock('\PDO');
$this->db = new PDOWrapper($this->pdoMock);
}
public function testWrapperExists() {
$this->pdoMock->method('getAttribute')->willReturn(\PDO::ERRMODE_EXCEPTION);
$db = new PDOWrapper($this->pdoMock);
$x = $db instanceof CRUDImp;
$this->assertTrue($x);
}
/**
* #expectedException \Exception
*/
public function testNonPDOPassedToConstructor() {
$mock = $this->createMock('\Exception');
$x = new PDOWrapper($mock);
}
...
}
Since PHP is loosely typed I check to make sure that the class passed to the constructor was an instance of \PDO. I implemented the concrete class as follows
class PDOWrapper implements CRUDImp {
private $pdo;
private $dataOutputType = \PDO::FETCH_ASSOC;
public function __construct($pdo) {
if (!($pdo instanceof \PDO)) {
throw new \Exception("PDOWrapper must be passed instance of \PDO");
}
$attr_Errmode = $pdo->getAttribute(\PDO::ATTR_ERRMODE);
if ($attr_Errmode !== \PDO::ERRMODE_EXCEPTION) {
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
$this->pdo = $pdo;
}
...
}
Now that I have an independent database wrapper the original Model tests are at the moment trivial and no longer needed. The abstract class Model was modified as follows:
abstract class Model {
protected $database=null;
function __construct(CRUDWrapper $database) {
$this->database = $database;
}
...
}
So for those not familiar with dependency injection I found the following links helpful:
http://php-di.org/doc/understanding-di.html
https://codeinphp.github.io/post/dependency-injection-in-php/
https://designpatternsphp.readthedocs.io/en/latest/Structural/DependencyInjection/README.html
Hope this shortens someone's work.
if I have to do multiple queries in a row, is it better to do this:
$connection = Yii::app()->db;
once... and then keep using $connection, or is there an overhead if I have multiple functions like this:
function mainFunction() {
$dbResult1 = dbresult1();
$dbResult2 = dbresult2();
$dbResult2 = dbresult3();
}
function dbresult1() {
$connection = Yii::app()->db;
// do stuff
return $result;
}
function dbresult2() {
$connection = Yii::app()->db;
// do stuff
return $result;
}
function dbresult3() {
$connection = Yii::app()->db;
// do stuff
return $result;
}
Would it be better to do this:
function mainFunction() {
$connection = Yii::app()->db;
// do stuff with $connection for $dbResult1
// do stuff with $connection for $dbResult2
// do stuff with $connection for $dbResult3
}
?
You can trace the source code to see what happens when you call Yii::app()->db:
Yii::app() returns the static app property of YiiBase. You can see the source code here.
public static function app()
{
return self::$_app;
}
Yii::app()->db here it gets more interesting, because something gets looked up. You can see the source code here.
public function getDb()
{
return $this->getComponent('db');
}
getComponent() is a method of CModule you find the source code here.
public function getComponent($id,$createIfNull=true)
{
if(isset($this->_components[$id]))
return $this->_components[$id];
elseif(isset($this->_componentConfig[$id]) && $createIfNull)
{
$config=$this->_componentConfig[$id];
if(!isset($config['enabled']) || $config['enabled'])
{
Yii::trace("Loading \"$id\" application component",'system.CModule');
unset($config['enabled']);
$component=Yii::createComponent($config);
$component->init();
return $this->_components[$id]=$component;
}
}
}
As you see Yii::app()->db results in a few method calls and an array lookup. If performance is highly critical you might should cache the db instance. Else I would target on writing clean and readable code and won't care about such little tweaks.
I just started studying programming about 6 months ago and I have really been diving deep into Objective-C. Unfortunately, I don't know any programmers IRL to bounce general questions off of.
What languages are being used when people write programs that will search a website for information and then send it back? For example, if I wanted to write a program that would search weather.com for the daily temperature of the last 30 days in a given location and then send it back as say...an NSArray or NSDictionary, how would i do that? Can I do that in Objective C or is that super-advanced scripting language stuff? If I CAN do it in Objective-C, can someone link to a tutorial or place that may get me started learning that type of stuff? (I don't really know the term for this type of programming so my google searches have been unfruitful.)
I most commonly use PHP and MySQL with CURL
http://en.wikipedia.org/wiki/CURL
You can do some fun things like Search Engine Results Page queries, etc.
Here is the source from a crawler I use. I've cut out some parts for anonymity's sake, but it's a good almost-working example. I can help you get it running if need be.
<?php
class Crawler {
protected $markup = '';
protected $uri = '';
protected $db_location = "localhost";
protected $db_username = "***";
protected $db_password = "***";
protected $db_name = "***";
public function __construct() {
ini_set('memory_limit', -1);
}
public function getMarkup() {
$markup = "";
$markup = #file_get_contents($this->uri);
return $markup;
}
public function get($type) {
$method = "_get_{$type}";
if (method_exists($this, $method)){
return call_user_method($method, $this);
}
}
protected function db_query($query) {
$connection = mysql_connect($this->db_location,$this->db_username,$this->db_password) or die(mysql_error());
mysql_select_db($this->db_name,$connection) or die(mysql_error()." >> ".$query);
//echo $query."<br/>"; //for debugging
$result = mysql_query($query,$connection) or die (mysql_error()." >> ".$query);
$i = 0;
if($result != 1)
{
while ($data_array = mysql_fetch_array($result))
{
foreach($data_array as $key => $value)
{
$tableArray[$i][$key] = stripslashes($data_array[$key]);
}
$i++;
}
return $tableArray;
}
}
protected function db_insert($table,$array) {
$tableArray = $this->db_query("show columns from ".$table);
$inputString = "";
foreach($tableArray as $key => $value)
{
if (array_key_exists($value[0], $array) && $value[0]) {
$inputString .= "'".addslashes($array[$value[0]])."', ";
} else {
$inputString .= "'', ";
}
}
$inputString = substr($inputString, 0, -2);
$this->db_query("insert into $table values(".$inputString.")");
return mysql_insert_id();
}
protected function _get_data() {
//$scrape['id'] = $this->get('id');
$scrape['name'] = $this->get('name');
$scrape['tags'] = $this->get('tags');
$scrape['stat_keys'] = $this->get('stat_keys');
$scrape['stat_values'] = $this->get('stat_values');
foreach($scrape['stat_values'] as $key => $value) {
$scrape['stat_values'][$key] = trim($scrape['stat_values'][$key]);
if(strpos($value,"<h5>Featured Product</h5>")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"<h5>Featured Company</h5>")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"<h5>Featured Type</h5>")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"sign in")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"/100")) {
unset($scrape['stat_values'][$key]);
}
}
if(sizeof($scrape['tags']) > 0 && is_array($scrape['tags'])) {
foreach($scrape['tags'] as $tag) {
$tag_array[$tag] = $tag_array[$tag] + 1;
}
$scrape['tags'] = $tag_array;
foreach($scrape['tags'] as $key => $tag_count) {
$scrape['tags'][$key] = $tag_count - 1;
}
}
$scrape['stat_values'] = array_merge(array(),$scrape['stat_values']);
return $scrape;
}
protected function _get_images() {
if (!empty($this->markup)){
preg_match_all('/<img([^>]+)\/>/i', $this->markup, $images);
return !empty($images[1]) ? $images[1] : FALSE;
}
}
protected function _get_links() {
if (!empty($this->markup)){
preg_match_all('/<a([^>]+)\>(.*?)\<\/a\>/i', $this->markup, $links);
return !empty($links[1]) ? $links[1] : FALSE;
}
}
protected function _get_id() {
if (!empty($this->markup)){
preg_match_all('/\/wine\/view\/([^`]*?)-/', $this->markup, $links);
return !empty($links[1]) ? $links[1] : FALSE;
}
}
protected function _get_grape() {
if (!empty($this->markup)){
preg_match_all('/ class="linked" style="font-size: 14px;">([^`]*?)<\/a>/', $this->markup, $links);
return !empty($links[1]) ? $links[1] : FALSE;
}
}
}
if($_GET['pass'] == "go") {
$crawl = new Crawler();
$crawl->go();
}
?>
So, you want to know how to write server-side code? Well, in theory you can write that in whatever you want. I also assure you it isn't "super-advanced".
You might find it easiest to get started with PHP. W3schools.com has a fine tutorial.
What you are describing is a crawler (e.g. Google).
Any language that has the ability to send HTTP requests and receive responses can do this (which is most languages).
If you don't care to code this thing from scratch, try downloading an open source crawler framework that will allow for custom plugins to parse the resulting HTML.
For your example, you would tell the crawler what site you want it to crawl (i.e. your weather site), add URI constraints if necessary, and create a custom plugin to parse the weather data out of the HTML it responds with. You can then save that data however you see fit.
I have the following PHP code;
<?php
component_customer_init();
component_customer_go();
function component_customer_init()
{
$customer = Customer::getInstance();
$customer->set(1);
}
function component_customer_go()
{
$customer = Customer::getInstance();
$customer->get();
}
class Customer
{
public $id;
static $class = false;
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
else
{
return self::$class;
}
}
public function set($id)
{
$this->id = $id;
}
public function get()
{
print $this->id;
}
}
?>
I get the following error;
Fatal error: Call to a member function set() on a non-object in /.../classes/customer.php on line 9
Can anyone tell me why I get this error? I know this code might look strange, but it's based on a component system that I'm writing for a CMS. The aim is to be able to replace HTML tags in the template e.g.;
<!-- component:customer-login -->
with;
<?php component_customer_login(); ?>
I also need to call pre-render methods of the "Customer" class to validate forms before output is made etc.
If anyone can think of a better way, please let me know but in the first instance, I'd like to know why I get the "Fatal error" mentioned above.
Well, I think your Customer::getInstance() method is flawed. It should look like this:
...
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
return self::$class; // ADDED!!
}
else
{
return self::$class;
}
}
....
In the if(self::$class == false) branch you are creating the instance of the class, but you dont return it.
You could also rewrite it as such:
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
return self::$class;
}
To make it a bit shorter.
DRY: Don't Repeat Yourself
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
return self::$class;
}
And for Sinlgetons it is also important to prevent __clone() from being used. Making it private should solve that problem:
private function __clone() {}