First commit

This commit is contained in:
Frédéric Guillot
2014-01-25 14:56:02 -05:00
commit 9383a15af6
80 changed files with 7519 additions and 0 deletions

1
vendor/.htaccess vendored Normal file
View File

@@ -0,0 +1 @@
Deny from all

20
vendor/Parsedown/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Emanuil Rusev, erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

991
vendor/Parsedown/Parsedown.php vendored Normal file
View File

@@ -0,0 +1,991 @@
<?php
#
#
# Parsedown
# http://parsedown.org
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, please view the LICENSE file that was
# distributed with this source code.
#
#
class Parsedown
{
#
# Multiton (http://en.wikipedia.org/wiki/Multiton_pattern)
#
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))
return self::$instances[$name];
$instance = new Parsedown();
self::$instances[$name] = $instance;
return $instance;
}
private static $instances = array();
#
# Setters
#
private $break_marker = " \n";
function set_breaks_enabled($breaks_enabled)
{
$this->break_marker = $breaks_enabled ? "\n" : " \n";
return $this;
}
#
# Fields
#
private $reference_map = array();
private $escape_sequence_map = array();
#
# Public Methods
#
function parse($text)
{
# removes UTF-8 BOM and marker characters
$text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
# removes \r characters
$text = str_replace("\r\n", "\n", $text);
$text = str_replace("\r", "\n", $text);
# replaces tabs with spaces
$text = str_replace("\t", ' ', $text);
# encodes escape sequences
if (strpos($text, '\\') !== FALSE)
{
$escape_sequences = array('\\\\', '\`', '\*', '\_', '\{', '\}', '\[', '\]', '\(', '\)', '\>', '\#', '\+', '\-', '\.', '\!');
foreach ($escape_sequences as $index => $escape_sequence)
{
if (strpos($text, $escape_sequence) !== FALSE)
{
$code = "\x1A".'\\'.$index.';';
$text = str_replace($escape_sequence, $code, $text);
$this->escape_sequence_map[$code] = $escape_sequence;
}
}
}
# ~
$text = preg_replace('/\n\s*\n/', "\n\n", $text);
$text = trim($text, "\n");
$lines = explode("\n", $text);
$text = $this->parse_block_elements($lines);
# decodes escape sequences
foreach ($this->escape_sequence_map as $code => $escape_sequence)
{
$text = str_replace($code, $escape_sequence[1], $text);
}
# ~
$text = rtrim($text, "\n");
return $text;
}
#
# Private Methods
#
private function parse_block_elements(array $lines, $context = '')
{
$elements = array();
$element = array(
'type' => '',
);
foreach ($lines as $line)
{
# fenced elements
switch ($element['type'])
{
case 'fenced_code_block':
if ( ! isset($element['closed']))
{
if (preg_match('/^[ ]*'.$element['fence'][0].'{3,}[ ]*$/', $line))
{
$element['closed'] = true;
}
else
{
$element['text'] !== '' and $element['text'] .= "\n";
$element['text'] .= $line;
}
continue 2;
}
break;
case 'markup':
if ( ! isset($element['closed']))
{
if (preg_match('{<'.$element['subtype'].'>$}', $line)) # opening tag
{
$element['depth']++;
}
if (preg_match('{</'.$element['subtype'].'>$}', $line)) # closing tag
{
$element['depth'] > 0
? $element['depth']--
: $element['closed'] = true;
}
$element['text'] .= "\n".$line;
continue 2;
}
break;
}
# *
if ($line === '')
{
$element['interrupted'] = true;
continue;
}
# composite elements
switch ($element['type'])
{
case 'blockquote':
if ( ! isset($element['interrupted']))
{
$line = preg_replace('/^[ ]*>[ ]?/', '', $line);
$element['lines'] []= $line;
continue 2;
}
break;
case 'li':
if (preg_match('/^([ ]{0,3})(\d+[.]|[*+-])[ ](.*)/', $line, $matches))
{
if ($element['indentation'] !== $matches[1])
{
$element['lines'] []= $line;
}
else
{
unset($element['last']);
$elements []= $element;
$element = array(
'type' => 'li',
'indentation' => $matches[1],
'last' => true,
'lines' => array(
preg_replace('/^[ ]{0,4}/', '', $matches[3]),
),
);
}
continue 2;
}
if (isset($element['interrupted']))
{
if ($line[0] === ' ')
{
$element['lines'] []= '';
$line = preg_replace('/^[ ]{0,4}/', '', $line);
$element['lines'] []= $line;
unset($element['interrupted']);
continue 2;
}
}
else
{
$line = preg_replace('/^[ ]{0,4}/', '', $line);
$element['lines'] []= $line;
continue 2;
}
break;
}
# indentation sensitive types
$deindented_line = $line;
switch ($line[0])
{
case ' ':
# ~
$deindented_line = ltrim($line);
if ($deindented_line === '')
{
continue 2;
}
# code block
if (preg_match('/^[ ]{4}(.*)/', $line, $matches))
{
if ($element['type'] === 'code_block')
{
if (isset($element['interrupted']))
{
$element['text'] .= "\n";
unset ($element['interrupted']);
}
$element['text'] .= "\n".$matches[1];
}
else
{
$elements []= $element;
$element = array(
'type' => 'code_block',
'text' => $matches[1],
);
}
continue 2;
}
break;
case '#':
# atx heading (#)
if (preg_match('/^(#{1,6})[ ]*(.+?)[ ]*#*$/', $line, $matches))
{
$elements []= $element;
$level = strlen($matches[1]);
$element = array(
'type' => 'h.',
'text' => $matches[2],
'level' => $level,
);
continue 2;
}
break;
case '-':
# setext heading (---)
if ($line[0] === '-' and $element['type'] === 'p' and ! isset($element['interrupted']) and preg_match('/^[-]+[ ]*$/', $line))
{
$element['type'] = 'h.';
$element['level'] = 2;
continue 2;
}
break;
case '=':
# setext heading (===)
if ($line[0] === '=' and $element['type'] === 'p' and ! isset($element['interrupted']) and preg_match('/^[=]+[ ]*$/', $line))
{
$element['type'] = 'h.';
$element['level'] = 1;
continue 2;
}
break;
}
# indentation insensitive types
switch ($deindented_line[0])
{
case '<':
# self-closing tag
if (preg_match('{^<.+?/>$}', $deindented_line))
{
$elements []= $element;
$element = array(
'type' => '',
'text' => $deindented_line,
);
continue 2;
}
# opening tag
if (preg_match('{^<(\w+)(?:[ ].*?)?>}', $deindented_line, $matches))
{
$elements []= $element;
$element = array(
'type' => 'markup',
'subtype' => strtolower($matches[1]),
'text' => $deindented_line,
'depth' => 0,
);
preg_match('{</'.$matches[1].'>\s*$}', $deindented_line) and $element['closed'] = true;
continue 2;
}
break;
case '>':
# quote
if (preg_match('/^>[ ]?(.*)/', $deindented_line, $matches))
{
$elements []= $element;
$element = array(
'type' => 'blockquote',
'lines' => array(
$matches[1],
),
);
continue 2;
}
break;
case '[':
# reference
if (preg_match('/^\[(.+?)\]:[ ]*(.+?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*$/', $deindented_line, $matches))
{
$label = strtolower($matches[1]);
$this->reference_map[$label] = array(
'»' => trim($matches[2], '<>'),
);
if (isset($matches[3]))
{
$this->reference_map[$label]['#'] = $matches[3];
}
continue 2;
}
break;
case '`':
case '~':
# fenced code block
if (preg_match('/^([`]{3,}|[~]{3,})[ ]*(\S+)?[ ]*$/', $deindented_line, $matches))
{
$elements []= $element;
$element = array(
'type' => 'fenced_code_block',
'text' => '',
'fence' => $matches[1],
);
isset($matches[2]) and $element['language'] = $matches[2];
continue 2;
}
break;
case '*':
case '+':
case '-':
case '_':
# hr
if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $deindented_line))
{
$elements []= $element;
$element = array(
'type' => 'hr',
);
continue 2;
}
# li
if (preg_match('/^([ ]*)[*+-][ ](.*)/', $line, $matches))
{
$elements []= $element;
$element = array(
'type' => 'li',
'ordered' => false,
'indentation' => $matches[1],
'last' => true,
'lines' => array(
preg_replace('/^[ ]{0,4}/', '', $matches[2]),
),
);
continue 2;
}
}
# li
if ($deindented_line[0] <= '9' and $deindented_line >= '0' and preg_match('/^([ ]*)\d+[.][ ](.*)/', $line, $matches))
{
$elements []= $element;
$element = array(
'type' => 'li',
'ordered' => true,
'indentation' => $matches[1],
'last' => true,
'lines' => array(
preg_replace('/^[ ]{0,4}/', '', $matches[2]),
),
);
continue;
}
# paragraph
if ($element['type'] === 'p')
{
if (isset($element['interrupted']))
{
$elements []= $element;
$element['text'] = $line;
unset($element['interrupted']);
}
else
{
$element['text'] .= "\n".$line;
}
}
else
{
$elements []= $element;
$element = array(
'type' => 'p',
'text' => $line,
);
}
}
$elements []= $element;
unset($elements[0]);
#
# ~
#
$markup = '';
foreach ($elements as $element)
{
switch ($element['type'])
{
case 'p':
$text = $this->parse_span_elements($element['text']);
if ($context === 'li' and $markup === '')
{
if (isset($element['interrupted']))
{
$markup .= "\n".'<p>'.$text.'</p>'."\n";
}
else
{
$markup .= $text;
}
}
else
{
$markup .= '<p>'.$text.'</p>'."\n";
}
break;
case 'blockquote':
$text = $this->parse_block_elements($element['lines']);
$markup .= '<blockquote>'."\n".$text.'</blockquote>'."\n";
break;
case 'code_block':
case 'fenced_code_block':
$text = htmlspecialchars($element['text'], ENT_NOQUOTES, 'UTF-8');
strpos($text, "\x1A\\") !== FALSE and $text = strtr($text, $this->escape_sequence_map);
$markup .= isset($element['language'])
? '<pre><code class="language-'.$element['language'].'">'.$text.'</code></pre>'
: '<pre><code>'.$text.'</code></pre>';
$markup .= "\n";
break;
case 'h.':
$text = $this->parse_span_elements($element['text']);
$markup .= '<h'.$element['level'].'>'.$text.'</h'.$element['level'].'>'."\n";
break;
case 'hr':
$markup .= '<hr />'."\n";
break;
case 'li':
if (isset($element['ordered'])) # first
{
$list_type = $element['ordered'] ? 'ol' : 'ul';
$markup .= '<'.$list_type.'>'."\n";
}
if (isset($element['interrupted']) and ! isset($element['last']))
{
$element['lines'] []= '';
}
$text = $this->parse_block_elements($element['lines'], 'li');
$markup .= '<li>'.$text.'</li>'."\n";
isset($element['last']) and $markup .= '</'.$list_type.'>'."\n";
break;
case 'markup':
$markup .= $this->parse_span_elements($element['text'])."\n";
break;
default:
$markup .= $element['text']."\n";
}
}
return $markup;
}
# ~
private $strong_regex = array(
'*' => '/^[*]{2}([^*]+?)[*]{2}(?![*])/s',
'_' => '/^__([^_]+?)__(?!_)/s',
);
private $em_regex = array(
'*' => '/^[*]([^*]+?)[*](?![*])/s',
'_' => '/^_([^_]+?)[_](?![_])\b/s',
);
private $strong_em_regex = array(
'*' => '/^[*]{2}(.*?)[*](.+?)[*](.*?)[*]{2}/s',
'_' => '/^__(.*?)_(.+?)_(.*?)__/s',
);
private $em_strong_regex = array(
'*' => '/^[*](.*?)[*]{2}(.+?)[*]{2}(.*?)[*]/s',
'_' => '/^_(.*?)__(.+?)__(.*?)_/s',
);
private function parse_span_elements($text, $markers = array('![', '&', '*', '<', '[', '_', '`', 'http', '~~'))
{
if (isset($text[2]) === false or $markers === array())
{
return $text;
}
# ~
$markup = '';
while ($markers)
{
$closest_marker = null;
$closest_marker_index = 0;
$closest_marker_position = null;
foreach ($markers as $index => $marker)
{
$marker_position = strpos($text, $marker);
if ($marker_position === false)
{
unset($markers[$index]);
continue;
}
if ($closest_marker === null or $marker_position < $closest_marker_position)
{
$closest_marker = $marker;
$closest_marker_index = $index;
$closest_marker_position = $marker_position;
}
}
# ~
if ($closest_marker === null or isset($text[$closest_marker_position + 2]) === false)
{
$markup .= $text;
break;
}
else
{
$markup .= substr($text, 0, $closest_marker_position);
}
$text = substr($text, $closest_marker_position);
# ~
unset($markers[$closest_marker_index]);
# ~
switch ($closest_marker)
{
case '![':
case '[':
if (strpos($text, ']') and preg_match('/\[((?:[^][]|(?R))*)\]/', $text, $matches))
{
$element = array(
'!' => $text[0] === '!',
'a' => $matches[1],
);
$offset = strlen($matches[0]);
$element['!'] and $offset++;
$remaining_text = substr($text, $offset);
if ($remaining_text[0] === '(' and preg_match('/\([ ]*(.*?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*\)/', $remaining_text, $matches))
{
$element['»'] = $matches[1];
if (isset($matches[2]))
{
$element['#'] = $matches[2];
}
$offset += strlen($matches[0]);
}
elseif ($this->reference_map)
{
$reference = $element['a'];
if (preg_match('/^\s*\[(.*?)\]/', $remaining_text, $matches))
{
$reference = $matches[1] ? $matches[1] : $element['a'];
$offset += strlen($matches[0]);
}
$reference = strtolower($reference);
if (isset($this->reference_map[$reference]))
{
$element['»'] = $this->reference_map[$reference]['»'];
if (isset($this->reference_map[$reference]['#']))
{
$element['#'] = $this->reference_map[$reference]['#'];
}
}
else
{
unset($element);
}
}
else
{
unset($element);
}
}
if (isset($element))
{
$element['»'] = str_replace('&', '&amp;', $element['»']);
$element['»'] = str_replace('<', '&lt;', $element['»']);
if ($element['!'])
{
$markup .= '<img alt="'.$element['a'].'" src="'.$element['»'].'" />';
}
else
{
$element['a'] = $this->parse_span_elements($element['a'], $markers);
$markup .= isset($element['#'])
? '<a href="'.$element['»'].'" title="'.$element['#'].'">'.$element['a'].'</a>'
: '<a href="'.$element['»'].'">'.$element['a'].'</a>';
}
unset($element);
}
else
{
$markup .= $closest_marker;
$offset = $closest_marker === '![' ? 2 : 1;
}
break;
case '&':
$markup .= '&amp;';
$offset = substr($text, 0, 5) === '&amp;' ? 5 : 1;
break;
case '*':
case '_':
if ($text[1] === $closest_marker and preg_match($this->strong_regex[$closest_marker], $text, $matches))
{
$matches[1] = $this->parse_span_elements($matches[1], $markers);
$markup .= '<strong>'.$matches[1].'</strong>';
}
elseif (preg_match($this->em_regex[$closest_marker], $text, $matches))
{
$matches[1] = $this->parse_span_elements($matches[1], $markers);
$markup .= '<em>'.$matches[1].'</em>';
}
elseif ($text[1] === $closest_marker and preg_match($this->strong_em_regex[$closest_marker], $text, $matches))
{
$matches[2] = $this->parse_span_elements($matches[2], $markers);
$matches[1] and $matches[1] = $this->parse_span_elements($matches[1], $markers);
$matches[3] and $matches[3] = $this->parse_span_elements($matches[3], $markers);
$markup .= '<strong>'.$matches[1].'<em>'.$matches[2].'</em>'.$matches[3].'</strong>';
}
elseif (preg_match($this->em_strong_regex[$closest_marker], $text, $matches))
{
$matches[2] = $this->parse_span_elements($matches[2], $markers);
$matches[1] and $matches[1] = $this->parse_span_elements($matches[1], $markers);
$matches[3] and $matches[3] = $this->parse_span_elements($matches[3], $markers);
$markup .= '<em>'.$matches[1].'<strong>'.$matches[2].'</strong>'.$matches[3].'</em>';
}
if (isset($matches) and $matches)
{
$offset = strlen($matches[0]);
}
else
{
$markup .= $closest_marker;
$offset = 1;
}
break;
case '<':
if (strpos($text, '>') !== false)
{
if ($text[1] === 'h' and preg_match('/^<(https?:[\/]{2}[^\s]+?)>/i', $text, $matches))
{
$element_url = $matches[1];
$element_url = str_replace('&', '&amp;', $element_url);
$element_url = str_replace('<', '&lt;', $element_url);
$markup .= '<a href="'.$element_url.'">'.$element_url.'</a>';
$offset = strlen($matches[0]);
}
elseif (preg_match('/^<\/?\w.*?>/', $text, $matches))
{
$markup .= $matches[0];
$offset = strlen($matches[0]);
}
else
{
$markup .= '&lt;';
$offset = 1;
}
}
else
{
$markup .= '&lt;';
$offset = 1;
}
break;
case '`':
if (preg_match('/^`(.+?)`/', $text, $matches))
{
$element_text = $matches[1];
$element_text = htmlspecialchars($element_text, ENT_NOQUOTES, 'UTF-8');
if ($this->escape_sequence_map and strpos($element_text, "\x1A") !== false)
{
$element_text = strtr($element_text, $this->escape_sequence_map);
}
$markup .= '<code>'.$element_text.'</code>';
$offset = strlen($matches[0]);
}
else
{
$markup .= '`';
$offset = 1;
}
break;
case 'http':
if (preg_match('/^https?:[\/]{2}[^\s]+\b/i', $text, $matches))
{
$element_url = $matches[0];
$element_url = str_replace('&', '&amp;', $element_url);
$element_url = str_replace('<', '&lt;', $element_url);
$markup .= '<a href="'.$element_url.'">'.$element_url.'</a>';
$offset = strlen($matches[0]);
}
else
{
$markup .= 'http';
$offset = 4;
}
break;
case '~~':
if (preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $text, $matches))
{
$matches[1] = $this->parse_span_elements($matches[1], $markers);
$markup .= '<del>'.$matches[1].'</del>';
$offset = strlen($matches[0]);
}
else
{
$markup .= '~~';
$offset = 2;
}
break;
}
if (isset($offset))
{
$text = substr($text, $offset);
}
$markers[$closest_marker_index] = $closest_marker;
}
$markup = str_replace($this->break_marker, '<br />'."\n", $markup);
return $markup;
}
}

108
vendor/PicoDb/Database.php vendored Normal file
View File

@@ -0,0 +1,108 @@
<?php
namespace PicoDb;
class Database
{
private $logs = array();
private $pdo;
public function __construct(array $settings)
{
if (! isset($settings['driver'])) {
throw new \LogicException('You must define a database driver.');
}
switch ($settings['driver']) {
case 'sqlite':
require_once __DIR__.'/Drivers/Sqlite.php';
$this->pdo = new Sqlite($settings['filename']);
break;
default:
throw new \LogicException('This database driver is not supported.');
}
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
public function setLogMessage($message)
{
$this->logs[] = $message;
}
public function getLogMessages()
{
return $this->logs;
}
public function getConnection()
{
return $this->pdo;
}
public function escapeIdentifier($value)
{
return $this->pdo->escapeIdentifier($value);
}
public function execute($sql, array $values = array())
{
try {
$this->setLogMessage($sql);
$this->setLogMessage(implode(', ', $values));
$rq = $this->pdo->prepare($sql);
$rq->execute($values);
return $rq;
}
catch (\PDOException $e) {
if ($this->pdo->inTransaction()) $this->pdo->rollback();
$this->setLogMessage($e->getMessage());
return false;
}
}
public function startTransaction()
{
$this->pdo->beginTransaction();
}
public function closeTransaction()
{
$this->pdo->commit();
}
public function cancelTransaction()
{
$this->pdo->rollback();
}
public function table($table_name)
{
require_once __DIR__.'/Table.php';
return new Table($this, $table_name);
}
public function schema()
{
require_once __DIR__.'/Schema.php';
return new Schema($this);
}
}

48
vendor/PicoDb/Drivers/Sqlite.php vendored Normal file
View File

@@ -0,0 +1,48 @@
<?php
namespace PicoDb;
class Sqlite extends \PDO {
public function __construct($filename)
{
parent::__construct('sqlite:'.$filename);
$this->exec('PRAGMA foreign_keys = ON');
}
public function getSchemaVersion()
{
$rq = $this->prepare('PRAGMA user_version');
$rq->execute();
$result = $rq->fetch(\PDO::FETCH_ASSOC);
if (isset($result['user_version'])) {
return $result['user_version'];
}
return 0;
}
public function setSchemaVersion($version)
{
$this->exec('PRAGMA user_version='.$version);
}
public function getLastId()
{
return $this->lastInsertId();
}
public function escapeIdentifier($value)
{
if (strpos($value, '.') !== false) return $value;
return '"'.$value.'"';
}
}

60
vendor/PicoDb/Schema.php vendored Normal file
View File

@@ -0,0 +1,60 @@
<?php
namespace PicoDb;
class Schema
{
protected $db = null;
public function __construct(Database $db)
{
$this->db = $db;
}
public function check($last_version = 1)
{
$current_version = $this->db->getConnection()->getSchemaVersion();
if ($current_version < $last_version) {
return $this->migrateTo($current_version, $last_version);
}
return true;
}
public function migrateTo($current_version, $next_version)
{
try {
$this->db->startTransaction();
for ($i = $current_version + 1; $i <= $next_version; $i++) {
$function_name = '\Schema\version_'.$i;
if (function_exists($function_name)) {
call_user_func($function_name, $this->db->getConnection());
$this->db->getConnection()->setSchemaVersion($i);
}
else {
throw new \LogicException('To execute a database migration, you need to create this function: "'.$function_name.'".');
}
}
$this->db->closeTransaction();
}
catch (\PDOException $e) {
$this->db->cancelTransaction();
return false;
}
return true;
}
}

430
vendor/PicoDb/Table.php vendored Normal file
View File

@@ -0,0 +1,430 @@
<?php
namespace PicoDb;
class Table
{
private $table_name = '';
private $sql_limit = '';
private $sql_offset = '';
private $sql_order = '';
private $joins = array();
private $conditions = array();
private $or_conditions = array();
private $is_or_condition = false;
private $columns = array();
private $values = array();
private $distinct = false;
private $group_by = array();
private $db;
public function __construct(Database $db, $table_name)
{
$this->db = $db;
$this->table_name = $table_name;
return $this;
}
public function save(array $data)
{
if (! empty($this->conditions)) {
return $this->update($data);
}
else {
return $this->insert($data);
}
}
public function update(array $data)
{
$columns = array();
$values = array();
foreach ($data as $column => $value) {
$columns[] = $this->db->escapeIdentifier($column).'=?';
$values[] = $value;
}
foreach ($this->values as $value) {
$values[] = $value;
}
$sql = sprintf(
'UPDATE %s SET %s %s',
$this->db->escapeIdentifier($this->table_name),
implode(', ', $columns),
$this->conditions()
);
$result = $this->db->execute($sql, $values);
if ($result !== false && $result->rowCount() > 0) {
return true;
}
return false;
}
public function insert(array $data)
{
$columns = array();
foreach ($data as $column => $value) {
$columns[] = $this->db->escapeIdentifier($column);
}
$sql = sprintf(
'INSERT INTO %s (%s) VALUES (%s)',
$this->db->escapeIdentifier($this->table_name),
implode(', ', $columns),
implode(', ', array_fill(0, count($data), '?'))
);
return false !== $this->db->execute($sql, array_values($data));
}
public function remove()
{
$sql = sprintf(
'DELETE FROM %s %s',
$this->db->escapeIdentifier($this->table_name),
$this->conditions()
);
return false !== $this->db->execute($sql, $this->values);
}
public function listing($key, $value)
{
$this->columns($key, $value);
$listing = array();
$results = $this->findAll();
if ($results) {
foreach ($results as $result) {
$listing[$result[$key]] = $result[$value];
}
}
return $listing;
}
public function findAll()
{
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
if (false === $rq) return false;
return $rq->fetchAll(\PDO::FETCH_ASSOC);
}
public function findAllByColumn($column)
{
$this->columns = array($column);
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
if (false === $rq) return false;
return $rq->fetchAll(\PDO::FETCH_COLUMN, 0);
}
public function findOne()
{
$this->limit(1);
$result = $this->findAll();
return isset($result[0]) ? $result[0] : null;
}
public function findOneColumn($column)
{
$this->limit(1);
$this->columns = array($column);
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
if (false === $rq) return false;
return $rq->fetchColumn();
}
public function buildSelectQuery()
{
return sprintf(
'SELECT %s %s FROM %s %s %s %s %s %s %s',
$this->distinct ? 'DISTINCT' : '',
empty($this->columns) ? '*' : implode(', ', $this->columns),
$this->db->escapeIdentifier($this->table_name),
implode(' ', $this->joins),
$this->conditions(),
empty($this->group_by) ? '' : 'GROUP BY '.implode(', ', $this->group_by),
$this->sql_order,
$this->sql_limit,
$this->sql_offset
);
}
public function count()
{
$sql = sprintf(
'SELECT COUNT(*) FROM %s'.$this->conditions().$this->sql_order.$this->sql_limit.$this->sql_offset,
$this->db->escapeIdentifier($this->table_name)
);
$rq = $this->db->execute($sql, $this->values);
if (false === $rq) return false;
$result = $rq->fetchColumn();
return $result ? (int) $result : 0;
}
public function join($table, $foreign_column, $local_column)
{
$this->joins[] = sprintf(
'LEFT JOIN %s ON %s=%s',
$this->db->escapeIdentifier($table),
$this->db->escapeIdentifier($table).'.'.$this->db->escapeIdentifier($foreign_column),
$this->db->escapeIdentifier($this->table_name).'.'.$this->db->escapeIdentifier($local_column)
);
return $this;
}
public function conditions()
{
if (! empty($this->conditions)) {
return ' WHERE '.implode(' AND ', $this->conditions);
}
else {
return '';
}
}
public function addCondition($sql)
{
if ($this->is_or_condition) {
$this->or_conditions[] = $sql;
}
else {
$this->conditions[] = $sql;
}
}
public function beginOr()
{
$this->is_or_condition = true;
$this->or_conditions = array();
return $this;
}
public function closeOr()
{
$this->is_or_condition = false;
if (! empty($this->or_conditions)) {
$this->conditions[] = '('.implode(' OR ', $this->or_conditions).')';
}
return $this;
}
public function orderBy($column, $order = 'ASC')
{
$order = strtoupper($order);
$order = $order === 'ASC' || $order === 'DESC' ? $order : 'ASC';
if ($this->sql_order === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order;
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.$order;
}
return $this;
}
public function asc($column)
{
if ($this->sql_order === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' ASC';
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' ASC';
}
return $this;
}
public function desc($column)
{
if ($this->sql_order === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' DESC';
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' DESC';
}
return $this;
}
public function limit($value)
{
if (! is_null($value)) $this->sql_limit = ' LIMIT '.(int) $value;
return $this;
}
public function offset($value)
{
if (! is_null($value)) $this->sql_offset = ' OFFSET '.(int) $value;
return $this;
}
public function groupBy()
{
$this->group_by = \func_get_args();
return $this;
}
public function columns()
{
$this->columns = \func_get_args();
return $this;
}
public function distinct()
{
$this->columns = \func_get_args();
$this->distinct = true;
return $this;
}
public function __call($name, array $arguments)
{
$column = $arguments[0];
$sql = '';
switch (strtolower($name)) {
case 'in':
if (isset($arguments[1]) && is_array($arguments[1])) {
$sql = sprintf(
'%s IN (%s)',
$this->db->escapeIdentifier($column),
implode(', ', array_fill(0, count($arguments[1]), '?'))
);
}
break;
case 'notin':
if (isset($arguments[1]) && is_array($arguments[1])) {
$sql = sprintf(
'%s NOT IN (%s)',
$this->db->escapeIdentifier($column),
implode(', ', array_fill(0, count($arguments[1]), '?'))
);
}
break;
case 'like':
$sql = sprintf('%s LIKE ?', $this->db->escapeIdentifier($column));
break;
case 'eq':
case 'equal':
case 'equals':
$sql = sprintf('%s = ?', $this->db->escapeIdentifier($column));
break;
case 'gt':
case 'greaterthan':
$sql = sprintf('%s > ?', $this->db->escapeIdentifier($column));
break;
case 'lt':
case 'lowerthan':
$sql = sprintf('%s < ?', $this->db->escapeIdentifier($column));
break;
case 'gte':
case 'greaterthanorequals':
$sql = sprintf('%s >= ?', $this->db->escapeIdentifier($column));
break;
case 'lte':
case 'lowerthanorequals':
$sql = sprintf('%s <= ?', $this->db->escapeIdentifier($column));
break;
case 'isnull':
$sql = sprintf('%s IS NULL', $this->db->escapeIdentifier($column));
break;
case 'notnull':
$sql = sprintf('%s IS NOT NULL', $this->db->escapeIdentifier($column));
break;
}
if ($sql !== '') {
$this->addCondition($sql);
if (isset($arguments[1])) {
if (is_array($arguments[1])) {
foreach ($arguments[1] as $value) {
$this->values[] = $value;
}
}
else {
$this->values[] = $arguments[1];
}
}
}
return $this;
}
}

44
vendor/SimpleValidator/Base.php vendored Normal file
View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
abstract class Base
{
protected $field = '';
protected $error_message = '';
protected $data = array();
abstract public function execute(array $data);
public function __construct($field, $error_message)
{
$this->field = $field;
$this->error_message = $error_message;
}
public function getErrorMessage()
{
return $this->error_message;
}
public function getField()
{
return $this->field;
}
}

67
vendor/SimpleValidator/Validator.php vendored Normal file
View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Validator
{
private $data = array();
private $errors = array();
private $validators = array();
public function __construct(array $data, array $validators)
{
$this->data = $data;
$this->validators = $validators;
}
public function execute()
{
$result = true;
foreach ($this->validators as $validator) {
if (! $validator->execute($this->data)) {
$this->addError(
$validator->getField(),
$validator->getErrorMessage()
);
$result = false;
}
}
return $result;
}
public function addError($field, $message)
{
if (! isset($this->errors[$field])) {
$this->errors[$field] = array();
}
$this->errors[$field][] = $message;
}
public function getErrors()
{
return $this->errors;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Alpha extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! ctype_alpha($data[$this->field])) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class AlphaNumeric extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! ctype_alnum($data[$this->field])) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Email extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
// I use the same validation method as Firefox
// http://hg.mozilla.org/mozilla-central/file/cf5da681d577/content/html/content/src/nsHTMLInputElement.cpp#l3967
$value = $data[$this->field];
$length = strlen($value);
// If the email address begins with a '@' or ends with a '.',
// we know it's invalid.
if ($value[0] === '@' || $value[$length - 1] === '.') {
return false;
}
// Check the username
for ($i = 0; $i < $length && $value[$i] !== '@'; ++$i) {
$c = $value[$i];
if (! (ctype_alnum($c) || $c === '.' || $c === '!' || $c === '#' || $c === '$' ||
$c === '%' || $c === '&' || $c === '\'' || $c === '*' || $c === '+' ||
$c === '-' || $c === '/' || $c === '=' || $c === '?' || $c === '^' ||
$c === '_' || $c === '`' || $c === '{' || $c === '|' || $c === '}' ||
$c === '~')) {
return false;
}
}
// There is no domain name (or it's one-character long),
// that's not a valid email address.
if (++$i >= $length) return false;
if (($i + 1) === $length) return false;
// The domain name can't begin with a dot.
if ($value[$i] === '.') return false;
// Parsing the domain name.
for (; $i < $length; ++$i) {
$c = $value[$i];
if ($c === '.') {
// A dot can't follow a dot.
if ($value[$i - 1] === '.') return false;
}
elseif (! (ctype_alnum($c) || $c === '-')) {
// The domain characters have to be in this list to be valid.
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Equals extends Base
{
private $field2;
public function __construct($field1, $field2, $error_message)
{
parent::__construct($field1, $error_message);
$this->field2 = $field2;
}
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! isset($data[$this->field2])) return false;
return $data[$this->field] === $data[$this->field2];
}
return true;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Integer extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (is_string($data[$this->field])) {
if ($data[$this->field][0] === '-') {
return ctype_digit(substr($data[$this->field], 1));
}
return ctype_digit($data[$this->field]);
}
else {
return is_int($data[$this->field]);
}
}
return true;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Ip extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! filter_var($data[$this->field], FILTER_VALIDATE_IP)) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Length extends Base
{
private $min;
private $max;
public function __construct($field, $error_message, $min, $max)
{
parent::__construct($field, $error_message);
$this->min = $min;
$this->max = $max;
}
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
$length = mb_strlen($data[$this->field], 'UTF-8');
if ($length < $this->min || $length > $this->max) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class MacAddress extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
$groups = explode(':', $data[$this->field]);
if (count($groups) !== 6) return false;
foreach ($groups as $group) {
if (! ctype_xdigit($group)) return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class MaxLength extends Base
{
private $max;
public function __construct($field, $error_message, $max)
{
parent::__construct($field, $error_message);
$this->max = $max;
}
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
$length = mb_strlen($data[$this->field], 'UTF-8');
if ($length > $this->max) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class MinLength extends Base
{
private $min;
public function __construct($field, $error_message, $min)
{
parent::__construct($field, $error_message);
$this->min = $min;
}
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
$length = mb_strlen($data[$this->field], 'UTF-8');
if ($length < $this->min) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Numeric extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! is_numeric($data[$this->field])) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Range extends Base
{
private $min;
private $max;
public function __construct($field, $error_message, $min, $max)
{
parent::__construct($field, $error_message);
$this->min = $min;
$this->max = $max;
}
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! is_numeric($data[$this->field])) {
return false;
}
if ($data[$this->field] < $this->min || $data[$this->field] > $this->max) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Required extends Base
{
public function execute(array $data)
{
if (! isset($data[$this->field]) || $data[$this->field] === '') {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,78 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
*/
class Unique extends Base
{
private $pdo;
private $primary_key;
private $table;
public function __construct($field, $error_message, \PDO $pdo, $table, $primary_key = 'id')
{
parent::__construct($field, $error_message);
$this->pdo = $pdo;
$this->primary_key = $primary_key;
$this->table = $table;
}
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
if (! isset($data[$this->primary_key])) {
$rq = $this->pdo->prepare('SELECT COUNT(*) FROM '.$this->table.' WHERE '.$this->field.'=?');
$rq->execute(array(
$data[$this->field]
));
$result = $rq->fetch(\PDO::FETCH_NUM);
if (isset($result[0]) && $result[0] === '1') {
return false;
}
}
else {
$rq = $this->pdo->prepare(
'SELECT COUNT(*) FROM '.$this->table.'
WHERE '.$this->field.'=? AND '.$this->primary_key.' != ?'
);
$rq->execute(array(
$data[$this->field],
$data[$this->primary_key]
));
$result = $rq->fetch(\PDO::FETCH_NUM);
if (isset($result[0]) && $result[0] === '1') {
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of Simple Validator.
*
* (c) Frédéric Guillot <contact@fredericguillot.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SimpleValidator\Validators;
use SimpleValidator\Base;
/**
* @author Frédéric Guillot <contact@fredericguillot.com>
* @link http://semver.org/
*/
class Version extends Base
{
public function execute(array $data)
{
if (isset($data[$this->field]) && $data[$this->field] !== '') {
$pattern = '/^[0-9]+\.[0-9]+\.[0-9]+([+-][^+-][0-9A-Za-z-.]*)?$/';
return (bool) preg_match($pattern, $data[$this->field]);
}
return true;
}
}

227
vendor/password.php vendored Normal file
View File

@@ -0,0 +1,227 @@
<?php
/**
* A Compatibility library with PHP 5.5's simplified password hashing API.
*
* @author Anthony Ferrara <ircmaxell@php.net>
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @copyright 2012 The Authors
*/
if (!defined('PASSWORD_BCRYPT')) {
define('PASSWORD_BCRYPT', 1);
define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
if (version_compare(PHP_VERSION, '5.3.7', '<')) {
define('PASSWORD_PREFIX', '$2a$');
}
else {
define('PASSWORD_PREFIX', '$2y$');
}
/**
* Hash the password using the specified algorithm
*
* @param string $password The password to hash
* @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
* @param array $options The options for the algorithm to use
*
* @return string|false The hashed password, or false on error.
*/
function password_hash($password, $algo, array $options = array()) {
if (!function_exists('crypt')) {
trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
return null;
}
if (!is_string($password)) {
trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
return null;
}
if (!is_int($algo)) {
trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
return null;
}
switch ($algo) {
case PASSWORD_BCRYPT:
// Note that this is a C constant, but not exposed to PHP, so we don't define it here.
$cost = 10;
if (isset($options['cost'])) {
$cost = $options['cost'];
if ($cost < 4 || $cost > 31) {
trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
return null;
}
}
$required_salt_len = 22;
$hash_format = sprintf("%s%02d$", PASSWORD_PREFIX, $cost);
break;
default:
trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
return null;
}
if (isset($options['salt'])) {
switch (gettype($options['salt'])) {
case 'NULL':
case 'boolean':
case 'integer':
case 'double':
case 'string':
$salt = (string) $options['salt'];
break;
case 'object':
if (method_exists($options['salt'], '__tostring')) {
$salt = (string) $options['salt'];
break;
}
case 'array':
case 'resource':
default:
trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
return null;
}
if (strlen($salt) < $required_salt_len) {
trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
return null;
} elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
$salt = str_replace('+', '.', base64_encode($salt));
}
} else {
$buffer = '';
$raw_length = (int) ($required_salt_len * 3 / 4 + 1);
$buffer_valid = false;
if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
$buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
if ($buffer) {
$buffer_valid = true;
}
}
if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
$buffer = openssl_random_pseudo_bytes($raw_length);
if ($buffer) {
$buffer_valid = true;
}
}
if (!$buffer_valid && is_readable('/dev/urandom')) {
$f = fopen('/dev/urandom', 'r');
$read = strlen($buffer);
while ($read < $raw_length) {
$buffer .= fread($f, $raw_length - $read);
$read = strlen($buffer);
}
fclose($f);
if ($read >= $raw_length) {
$buffer_valid = true;
}
}
if (!$buffer_valid || strlen($buffer) < $raw_length) {
$bl = strlen($buffer);
for ($i = 0; $i < $raw_length; $i++) {
if ($i < $bl) {
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
} else {
$buffer .= chr(mt_rand(0, 255));
}
}
}
$salt = str_replace('+', '.', base64_encode($buffer));
}
$salt = substr($salt, 0, $required_salt_len);
$hash = $hash_format . $salt;
$ret = crypt($password, $hash);
if (!is_string($ret) || strlen($ret) <= 13) {
return false;
}
return $ret;
}
/**
* Get information about the password hash. Returns an array of the information
* that was used to generate the password hash.
*
* array(
* 'algo' => 1,
* 'algoName' => 'bcrypt',
* 'options' => array(
* 'cost' => 10,
* ),
* )
*
* @param string $hash The password hash to extract info from
*
* @return array The array of information about the hash.
*/
function password_get_info($hash) {
$return = array(
'algo' => 0,
'algoName' => 'unknown',
'options' => array(),
);
if (substr($hash, 0, 4) == PASSWORD_PREFIX && strlen($hash) == 60) {
$return['algo'] = PASSWORD_BCRYPT;
$return['algoName'] = 'bcrypt';
list($cost) = sscanf($hash, PASSWORD_PREFIX."%d$");
$return['options']['cost'] = $cost;
}
return $return;
}
/**
* Determine if the password hash needs to be rehashed according to the options provided
*
* If the answer is true, after validating the password using password_verify, rehash it.
*
* @param string $hash The hash to test
* @param int $algo The algorithm used for new password hashes
* @param array $options The options array passed to password_hash
*
* @return boolean True if the password needs to be rehashed.
*/
function password_needs_rehash($hash, $algo, array $options = array()) {
$info = password_get_info($hash);
if ($info['algo'] != $algo) {
return true;
}
switch ($algo) {
case PASSWORD_BCRYPT:
$cost = isset($options['cost']) ? $options['cost'] : 10;
if ($cost != $info['options']['cost']) {
return true;
}
break;
}
return false;
}
/**
* Verify a password against a hash using a timing attack resistant approach
*
* @param string $password The password to verify
* @param string $hash The hash to verify against
*
* @return boolean If the password matches the hash
*/
function password_verify($password, $hash) {
if (!function_exists('crypt')) {
trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
return false;
}
$ret = crypt($password, $hash);
if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
return false;
}
$status = 0;
for ($i = 0; $i < strlen($ret); $i++) {
$status |= (ord($ret[$i]) ^ ord($hash[$i]));
}
return $status === 0;
}
}