The reason that the exception message was thrown in the case presented by QA team is when "superman" term came at the start of string, strpos gives 0
bcoz it founded it at the start position, hence given 0
to the if
condition with ! (not)
operator makes it true
.
Solution:
<?php
if (strpos(strtolower($str), 'superman') === false) {
throw new Exception;
}
When strpos
does not found any needle string in the haysack string it returns false
(boolean) result. So, its always better to use the ===
operator with the false
bool to make sure the result is returning boolean false as expected.
By looking at the Explain
query result, the column type=ALL
shows that it scanned the entire table to find the matching row, hence no index was used.
Solution:
CREATE INDEX idx_email ON users(email)
OR
CREATE UNIQUE INDEX idx_email ON users(email);
Implementing/creating an index on the table will make our query significantly faster as it does not have to scan or go thru all records of the table, it will only go thru the index tree.
Starting from PHP5.5, it started providing native hashing API/functions which is a nice wrapper around some password hashing algorithms, and its definitely easier to use than crypt()
functions
To hash the password:
<?php
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
To re-check/verify the hashed password:
<?php
if (password_verify($password, $hashedPassword)) {
// Success!
}
else {
// Invalid credentials
}
For projects prior to PHP5.5:
Use openwall's (phpass)[https://www.openwall.com/phpass/] library, its 0.4 version
supports way old PHP3 to PHP5.
Keeping the performance in mind usually I follow the principle, if the array is not sorted then use built-in array_search / in_array
, if sorted then use binary-search, if I have to search the same array multiple times say 20-30 times then use combination of array_flip() & isset()
. Few years ago someone asked me why php don't have built-in binary-search, I have no answers then, still today I couldn't find any-built-in function. so, here is my implementation below:
function binarySearch($array, $value)
{
$left = 0;
$right = count($array) - 1;
while ($left <= $right) {
$center = (int) floor(($left + $right) / 2);
if ($array[$center] < $value) {
$left = $center + 1;
} elseif ($array[$center] > $value) {
$right = $center - 1;
} else {
return $center;
}
}
return false;
}
Solution explanation:
The idea is, when searching of our value, we are cutting the array in half. If the center value is less than our searching value then it might be in the second half, if center value is bigger than the searched value then it might be in the first half, then repeat the steps until we find the value. Cutting the array in half everytime gives us benefit of saving CPU cycles. Hence, it gives us algorithm complexity of O(log n)
.
The issue that you are using more memory than its allowed, is because fetchAll
is retrieving everything from the database tables and putting it in the memory location $results
, which is clearly not an optimized solution when dealing with big tables. Below is my solution:
<?php
$stmt = $pdo->prepare('SELECT * FROM largeTable');
$stmt->execute();
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
// manipulate the data here
}
Solution explanation:
We can replace fetchAll()
with fetch()
, which only retrieves one row at a time, hence we are not hitting the memory limit and can also traverse the entire table.
Solution:
<?php
function phoneNumberFormat($number, $delimeter = '-', $format = [3,3,4]) {
// Strip out everything except numbers
$formattedNumber = preg_replace("/[^0-9]/", "", $number);
// throw exception if number is not in right length
if(strlen($formattedNumber) !== array_sum($format)) {
throw new \Exception('Number digits are not in expected length');
}
// Add back with delimeter in the given format
$result = [];
$position = 0;
foreach ($format as $key => $item){
$result[] = substr($formattedNumber, $position, $item);
$position += $item;
}
return implode($delimeter, $result);
}
First we create an interface for the cache classes with the two methods for storing and retrieving
<?php
namespace Acme;
interface CacheInterface
{
public function get($key);
public function set($key, $value);
}
Create the different cache classes that implement the above interface
<?php
namespace Acme;
class MemCache implements CacheInterface
{
private $cache;
public function __construct()
{
$this->cache = new Memcache();
$this->cache->connect('localhost', 11211);
}
public function get($key)
{
return $this->cache->get($key);
}
public function set($key, $value, $ttl = '0')
{
return $this->cache->set($key, $value, 0, $ttl);
}
}
class ApcCache implements CacheInterface
{
public function get($key)
{
return apc_fetch($key);
}
public function set($key, $value, $ttl = '0')
{
return apc_store($key, $value, 0, $ttl);
}
}
class DummyCache implements CacheInterface
{
public function get($key)
{
return false;
}
public function set($key, $value)
{
return true;
}
}
Then we can inject the cache class as a dependency to any class that wanted to use Cache service.
<?php
class Users
{
private $cache;
private $db;
public function __contruct(CacheInterface $cache, Database $db)
{
$this->cache = $cache;
$this->db = $db;
}
public function getList()
{
if ($usersList = $this->cache->get('USERS_LIST')===false) {
$usersList = $this->db->query('select * from users');
$this->cache->set('USERS_LIST', $usersList);
}
return $usersList;
}
}
Notice the dependencies of the class are injected through the constructor method. The $cache
object is type-hinted with CacheInterface
, so it could accept any cache class that implements that interface.
Lastly, we can initialize the cache class according the environment being used, and pass it to the Users
object initialization.
<?php
# config.php (production)
// $cache = new MemCache;
# config.php (staging)
// $cache = new ApcCache;
# config.php (dev)
$cache = new DummyCache;
$users = new Users($cache, $db);
$users->getList();
An alternate way could be, normally when we are using php frameworks or any dependency injection libraries like "php-di"/"pimple", we use containers
at the application level in which we bind our "abstract" classes with "concrete" implementations, then our containers will inject those concrete implementations to the required classes for us. Hence, achieving the IoC
inversion of control. So as an example, using Laravel's container our implementation will look like:
<?php
$container = Illuminate\container::getInstance();
// Bind CacheInterface
if ($environment == 'production')
$container->bind('Acme\CacheInterface', 'Acme\MemCache');
elseif ($environment == 'staging')
$container->bind('Acme\CacheInterface', 'Acme\ApcCache');
else
$container->bind('Acme\CacheInterface', 'Acme\DummyCache');
// Bind Models
$container->bind('Acme\Users');
// Initiate Users
$users = $container->make('Users');
When we ask the container to initiate/make the Users
class, it will automatically inject and resolve all the dependencies of the class.
PHPUnit tests for the provided FizzBuzz function
<?php
namespace Tests;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
class FizzBuzzTest extends TestCase
{
/**
* @test
*/
public function input_shouldBeExceptionIfStartIsLessThan0()
{
$this->expectException(InvalidArgumentException::class);
fizzBuzz(-1,10);
}
/**
* @test
*/
public function input_shouldBeExceptionIfStopIsLessThan0()
{
$this->expectException(InvalidArgumentException::class);
fizzBuzz(1,-1);
}
/**
* @test
*/
public function input_shouldBeExceptionIfStopIsLessStart()
{
$this->expectException(InvalidArgumentException::class);
fizzBuzz(10, 5);
}
/**
* @test
*/
public function output_shouldBeFizzBuzzIfStartStopIsZero()
{
$output = fizzBuzz(0, 0);
$this->assertEquals('FizzBuzz', $output);
}
/**
* @test
*/
public function output_shouldBeFizzWhenDivisibleBy3()
{
$output = fizzBuzz(1,100);
$this->stringOutputTester($output, 1, 100, 'Fizz');
}
/**
* @test
*/
public function output_shouldBeBuzzWhenDivisibleBy5()
{
$output = fizzBuzz(1,100);
$this->stringOutputTester($output, 1, 100, 'Buzz');
}
/**
* @test
*/
public function output_shouldBeFizzBuzzWhenDivisibleBy3And5()
{
$output = fizzBuzz(1,100);
$this->stringOutputTester($output, 1, 100, 'FizzBuzz');
}
/**
* @test
*/
public function output_shouldBeNumberWhenNotDivisibleBy3And5()
{
$output = fizzBuzz(1,100);
$this->stringOutputTester($output, 1, 100, 'Number');
}
protected function stringOutputTester($output, $start, $stop, $assertCase)
{
$pieces = [];
foreach(range($start, $stop) as $step) {
if ($this->isDivisibleBy($step, 3) && $this->isDivisibleBy($step, 5)) {
$expect = 'FizzBuzz';
}
if ($this->isDivisibleBy($step, 3) && !$this->isDivisibleBy($step, 5)) {
$expect = 'Fizz';
}
if (!$this->isDivisibleBy($step, 3) && $this->isDivisibleBy($step, 5)) {
$expect = 'Buzz';
}
if (!$this->isDivisibleBy($step, 3) && !$this->isDivisibleBy($step, 5)) {
$expect = $step;
}
$piece = substr($output, 0, strlen($expect));
if ($assertCase == $expect) {
$this->assertEquals($expect, $piece);
}
if ($assertCase == 'Number' && is_numeric($expect)) {
$this->assertEquals($expect, $piece);
$this->assertIsNumeric(filter_var($piece, FILTER_VALIDATE_INT));
}
$pieces[] = $piece;
$output = substr($output, strlen($piece));
}
$this->assertCount(($stop-($start-1)), $pieces, 'Something is wrong pieces in string is not matching expected steps');
}
protected function isDivisibleBy($number, $divisor)
{
return $number % $divisor === 0;
}
}
If I would be able to look at the Select
class then I would be more confident about my answer, but my guess is that PDO execute statement is expecting string so it could bind the values to the params in the sql statement, in this case you are providing object pdo didn't know how to convert that object into string. The solution is, you can tailor how your Select
object is represented as a string by implementing a __toString()
method in your class, so that when your object is type cast as a string (explicit type cast $str = (string) $myObject;
, or automatic echo $myObject
) you can control what is included and the string format.
Example:
<?php
class Select
{
protected $name;
public function __toString() {
return $this->name;
}
}
$select = new Select();
PDO::execute((string)$select);
Without nested loops, another way to do is using regular expression. First, convert the $files
list to the string, then convert all the $exclude
list to the regular expressions, so it can be used to replace/remove the files from the string. An example implementation is given below:
<?php
// convert files list to string
$fileStr = implode(PHP_EOL,$files);
// convert exclude list to the regular expression
$excludedPatterns = array_map(function ($excludedFile) {
$isfile = (stripos($excludedFile,'.')!==false);
return '/'. $excludedFile .($isfile ? '' : '\/.*').'$/m';
}, str_replace('/', '\/', $exclude));
// replace/remove the files
$fileStr = preg_replace($excludedPatterns, '', $fileStr);
// put the files string back together
$files = array_filter(explode(PHP_EOL, $fileStr));