Advertisement
Guest User

Untitled

a guest
Jul 4th, 2017
476
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 13.03 KB | None | 0 0
  1. <?php
  2.  
  3. define('DS', DIRECTORY_SEPARATOR);
  4. define('BASE_PATH', __DIR__);
  5. define('CLASS_PATH', BASE_PATH . DS . 'classes');
  6. define('TEMPLATE_PATH', BASE_PATH . DS . 'templates');
  7.  
  8.  
  9. function writeln($s) {
  10.   echo $s . '<br>';
  11. }
  12.  
  13.  
  14. function dump($v) {
  15.   echo '<pre>' . print_r($v, 1) . '</pre>';
  16. }
  17.  
  18.  
  19. function registry($k, $v = null) {
  20.   static $r = [];
  21.   return func_num_args() > 1 ? $r[$k] = $v : $r[$k] ?? null;
  22. }
  23.  
  24.  
  25. function render($filename, $data = []) {
  26.   extract($data);
  27.   require TEMPLATE_PATH . DS . $filename . '.php';
  28. }
  29.  
  30.  
  31. function error(int $code, callable $callback = null) {
  32.   static $callbacks = [];
  33.   if ($callback) {
  34.     return $callbacks[$code] = $callback;
  35.   }
  36.   http_response_code($code);
  37.   if (isset($callbacks[$code])) {
  38.     $callbacks[$code]();
  39.   }
  40. }
  41.  
  42. /*
  43. // Модифицированная версия https://gist.github.com/Xeoncross/5357205
  44. function route(string $path, string $method = $_SERVER['REQUEST_METHOD'],  callable $callback = null) {
  45.   static $routes = [];
  46.   if ($callback) {
  47.     // shortcuts
  48.     $path = str_replace(
  49.       ['(:any)', '(:id)', '(:year)', '(:month)', '(:day)'],
  50.       ['([^/]+)', '(\d+)', '(\d{4})', '(0[1-9]|1[0-2])', '([0-2][1-9]|30|31)'],
  51.       $path
  52.     );
  53.     // ...
  54.     return $routes[$method]["#^{$path}$#"] = $callback;
  55.   }
  56.   if (isset($routes[$method])) {
  57.     foreach ($routes[$method] as $regex => $callback) {
  58.       if (preg_match($regex, $path, $matches)) {
  59.         return $callback(...array_slice($matches, 1));
  60.       }
  61.     }
  62.   }
  63.   error(404);
  64. }
  65.  
  66.  
  67. route('/hello/(:any)', 'GET', function ($name) {
  68.   echo "Hello {$name}!";
  69. });
  70.  
  71. route('/hello/World');
  72.  
  73.  
  74. error(404, function () {
  75.   echo 'Page Not Found';
  76. });
  77.  
  78. route('/page-no-exists');
  79. */
  80.  
  81.  
  82. class Set implements Countable, IteratorAggregate {
  83.  
  84.   protected $data = [];
  85.  
  86.   public function __construct($data = []) {
  87.     $this->update($data);
  88.   }
  89.  
  90.   public function has($v) {
  91.     return in_array($v, $this->data, true);
  92.   }
  93.  
  94.   public function add($v) {
  95.     if (!$this->has($v)) {
  96.       $this->data[] = $v;
  97.     }
  98.   }
  99.  
  100.   public function update(array $data) {
  101.     foreach ($data as $v) {
  102.       $this->add($v);
  103.     }
  104.   }
  105.  
  106.   public function remove($v) {
  107.     $i = array_search($v, $this->data, true);
  108.     if (false !== $i) {
  109.       unset($this->data[$i]);
  110.       // Можно закомментировать, если не нужны индексы, идущие по порядку
  111.       $this->reindex();
  112.     }
  113.   }
  114.  
  115.   public function clear() {
  116.     $this->data = [];
  117.   }
  118.  
  119.   public function count() {
  120.     return count($this->data);
  121.   }
  122.  
  123.   public function getIterator() {
  124.     $obj = new ArrayObject($this->data);
  125.     return $obj->getIterator();
  126.   }
  127.  
  128.   public function toArray() {
  129.     return $this->data;
  130.   }
  131.  
  132.   protected function reindex() {
  133.     $this->data = array_values($this->data);
  134.   }
  135. }
  136.  
  137.  
  138. // ...
  139.  
  140. /**
  141.  * TODO: переписать.
  142.  */
  143. class DB {
  144.  
  145.   protected $dbname;
  146.   protected $user;
  147.   protected $password;
  148.   protected $host;
  149.   protected $port;
  150.   protected $charset;
  151.   protected $connection;
  152.   protected $statement;
  153.   protected $queries = [];
  154.  
  155.   public function __construct($dbname, $user = 'root', $password = '',
  156.         $host = 'localhost', $port = 3306, $charset = 'utf8') {
  157.     $this->dbname = $dbname;
  158.     $this->user = $user;
  159.     $this->password = $password;
  160.     $this->host = $host;
  161.     $this->port = $port;
  162.     $this->charset = $charset;
  163.   }
  164.  
  165.   public function connect() {
  166.     if ($this->connection) {
  167.       return;
  168.     }
  169.     $dsn = sprintf(
  170.       'mysql:dbname=%s;host=%s;port=%d;charset=%s',
  171.       $this->dbname,
  172.       $this->host,
  173.       $this->port,
  174.       $this->charset
  175.     );
  176.     try {
  177.       $this->connection = new PDO($dsn, $this->user, $this->password, [
  178.         PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  179.         PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
  180.       ]);
  181.     } catch (PDOException $e) {
  182.       throw new RuntimeException($e->getMessage());
  183.     }
  184.   }
  185.  
  186.   public function disconnect() {
  187.     $this->connection = null;
  188.   }
  189.  
  190.   public function query($sql, $bind = []) {
  191.     // Подключаемся при первом запросе
  192.     $this->connect();
  193.     try {
  194.       $t = -microtime(1);
  195.       $this->statement = $this->connection->prepare($sql);
  196.       $this->statement->execute($bind);
  197.       $t += microtime(1);
  198.       $this->queries[$sql] = $t;
  199.       return $this;
  200.     } catch (PDOException $e) {
  201.       throw new RuntimeException($e->getMessage());
  202.     }
  203.   }
  204.  
  205.   function select($table, $fields = '*', $conditions = [], $limit = 0,
  206.         $offset = 0, $sort = []) {
  207.     if (is_array($fields)) {
  208.       $fields = array_map([$this, 'quoteIdentifier'], $fields);
  209.       $fields = implode(', ', $fields);
  210.     }
  211.     $bind = array_values($conditions);
  212.     $where = [];
  213.     foreach (array_keys($conditions) as $column) {
  214.       $where[] = $this->quoteIdentifier($column) . ' = ?';
  215.     }
  216.     $order_by = [];
  217.     foreach ($sort as $column => $order) {
  218.       $order = strtoupper(trim($order));
  219.       $order = $order === 'ASC' ? $order : 'DESC';
  220.       $order_by[] = sprintf('%s %s', $this->quoteIdentifier($column), $order);
  221.     }
  222.     $sql = sprintf('SELECT %s FROM %s', $fields, $this->quoteIdentifier($table))
  223.       . ($where ? ' WHERE ' . implode(' AND ', $where) : '')
  224.       . ($order_by ? ' ORDER BY ' . implode(', ', $order_by) : '')
  225.       . ($limit
  226.           ? sprintf(' LIMIT %d, %d', $offset, $limit)
  227.           : $offset
  228.             ? sprintf(' OFFSET %d', $offset)
  229.             : '')
  230.       . ';';
  231.     $this->query($sql, $bind);
  232.     return $this;
  233.   }
  234.  
  235.   public function insert($table, $data) {
  236.     $sql = sprintf(
  237.       'INSERT INTO %s (%s) VALUES (%s);',
  238.       $this->quoteIdentifier($table),
  239.       implode(', ', array_map([$this, 'quoteIdentifier'], array_keys($data))),
  240.       substr(str_repeat('?, ', count($data)), 0, -2)
  241.     );
  242.     $this->query($sql, array_values($data));
  243.     return $this->lastInsertId();
  244.   }
  245.  
  246.   public function update($table, $data, $conditions = []) {
  247.     $bind = array_values($data);
  248.     $set = [];
  249.     foreach (array_keys($data) as $column) {
  250.       $set[] = $this->quoteIdentifier($column) . ' = ?';
  251.     }
  252.     $where = [];
  253.     foreach ($conditions as $column => $value) {
  254.       $where[] = $this->quoteIdentifier($column) . ' = ?';
  255.       $bind[] = $value;
  256.     }
  257.     $sql = sprintf(
  258.         'UPDATE %s SET %s',
  259.         $this->quoteIdentifier($table),
  260.         implode(', ', $set)
  261.       )
  262.       . ($where ? ' WHERE ' . implode(' AND ', $where) : '')
  263.       . ';';
  264.     return $this->query($sql, $bind)->affectedRows();
  265.   }
  266.  
  267.   public function delete($table, $conditions = []) {
  268.     $bind = array_values($conditions);
  269.     $where = [];
  270.     foreach (array_keys($conditions) as $column) {
  271.       $where[] = $this->quoteIdentifier($column) . ' = ?';
  272.     }
  273.     $sql = sprintf('DELETE FROM %s', $this->quoteIdentifier($table))
  274.       . ($where ? ' WHERE ' . implode(' AND ', $where) : '')
  275.       . ';';
  276.     return $this->query($sql, $bind)->affectedRows();
  277.   }
  278.  
  279.   public function fetch() {
  280.     try {
  281.       return $this->statement->fetch();
  282.     } catch (PDOException $e) {
  283.       throw new RuntimeException($e->getMessage());
  284.     }
  285.   }
  286.  
  287.   public function fetchOne() {
  288.     try {
  289.       return $this->statement->fetchColumn();
  290.     } catch (PDOException $e) {
  291.       throw new RuntimeException($e->getMessage());
  292.     }
  293.   }
  294.  
  295.   public function fetchAll() {
  296.     try {
  297.       return $this->statement->fetchAll();
  298.     } catch (PDOException $e) {
  299.       throw new RuntimeException($e->getMessage());
  300.     }
  301.   }
  302.  
  303.   public function affectedRows() {
  304.     try {
  305.       return $this->statement->rowCount();
  306.      } catch (PDOException $e) {
  307.       throw new RuntimeException($e->getMessage());
  308.     }
  309.   }
  310.  
  311.   public function lastInsertId() {
  312.     try {
  313.       return $this->connection->lastInsertId();
  314.     } catch (PDOException $e) {
  315.       throw new RuntimeException($e->getMessage());
  316.     }
  317.   }
  318.  
  319.   public function beginTransaction() {
  320.     try {
  321.       return $this->connection->beginTransaction();
  322.     } catch (PDOException $e) {
  323.       throw new RuntimeException($e->getMessage());
  324.     }
  325.   }
  326.  
  327.   public function commit() {
  328.     try {
  329.       return $this->connection->commit();
  330.     } catch (PDOException $e) {
  331.       throw new RuntimeException($e->getMessage());
  332.     }
  333.   }
  334.  
  335.   public function rollBack() {
  336.     try {
  337.       return $this->connection->rollBack();
  338.     } catch (PDOException $e) {
  339.       throw new RuntimeException($e->getMessage());
  340.     }
  341.   }
  342.  
  343.   public function quoteIdentifier($name) {
  344.     return '`' . str_replace('`', '``', $name) . '`';
  345.   }
  346.  
  347.   public function getQueries() {
  348.     return $this->queries;
  349.   }
  350.  
  351.   public function __destruct() {
  352.     $this->disconnect();
  353.   }
  354. }
  355.  
  356.  
  357. abstract class Entity implements IteratorAggregate {
  358.  
  359.   protected $data;
  360.  
  361.   public function __construct(array $data = []) {
  362.     $this->data = $data;
  363.   }
  364.  
  365.   public function __set($property, $value) {
  366.     $this->data[$property] = $value;
  367.   }
  368.  
  369.   public function __get($property) {
  370.     return $this->data[$property] ?? null;
  371.   }
  372.  
  373.   public function __isset($property) {
  374.     return isset($this->data[$property]);
  375.   }
  376.  
  377.   public function __unset($property) {
  378.     unset($this->data[$property]);
  379.   }
  380.  
  381.   public function getIterator() {
  382.     $obj = new ArrayObject($this->data);
  383.     return $obj->getIterator();
  384.   }
  385.  
  386.   public function toArray() {
  387.     return $this->data;
  388.   }
  389. }
  390.  
  391.  
  392. // Код ужасен
  393. abstract class EntityMapper {
  394.  
  395.   protected $db;
  396.   protected $fields = [];
  397.   protected $primaryKey;
  398.   // Эти свойства переопределяются в классах наследниках
  399.   protected $table;
  400.   protected $entitylClass;
  401.  
  402.   public function __construct($db) {
  403.     $this->db = $db;
  404.     $this->describeFields();
  405.   }
  406.  
  407.   public function find($id) {
  408.     $row = $this->db->select(
  409.       $this->table,
  410.       '*',
  411.       [$this->primaryKey => $id]
  412.     )->fetch();
  413.     return $row
  414.       ? $this->createEntity($row)
  415.       : null;
  416.   }
  417.  
  418.   public function findAll(array $conditions = [], int $limit = 0,
  419.         int $offset = 0, array $sort = []): array {
  420.     $rows = $this->db->select(
  421.       $this->table,
  422.       '*',
  423.       $conditions,
  424.       $limit,
  425.       $offset,
  426.       $sort
  427.     )->fetchAll();
  428.     return $this->createEntities($rows);
  429.   }
  430.  
  431.   public function save(Entity $entry): bool {
  432.     $data = [];
  433.     foreach ($this->fields as $field) {
  434.       $data[$field] = $entry->$field;
  435.     }
  436.     if ($entry->{$this->primaryKey}) {
  437.       unset($data[$this->primaryKey]);
  438.       $affected = $this->db->update(
  439.         $this->table,
  440.         $data,
  441.         [$this->primaryKey => $entry->{$this->primaryKey}]
  442.       );
  443.       return $affected === 1;
  444.     }
  445.     $id = $this->db->insert($this->table, $data);
  446.     if ($id) {
  447.       $entry->{$this->primaryKey} = $id;
  448.       return true;
  449.     }
  450.     return false;
  451.   }
  452.  
  453.   public function delete(Entity $entry): bool {
  454.     $affected = $this->db->delete(
  455.       $this->table,
  456.       [$this->primaryKey => $entry->{$this->primaryKey}]
  457.     );
  458.     if ($affected) {
  459.       $entry->{$this->primaryKey} = null;
  460.       return true;
  461.     }
  462.     return false;
  463.   }
  464.  
  465.   protected function describeFields() {
  466.     $sql = sprintf('DESCRIBE %s;', $this->db->quoteIdentifier($this->table));
  467.     $rows = $this->db->query($sql)->fetchAll();
  468.     foreach ($rows as $row) {
  469.       $this->fields[] = $row['Field'];
  470.       if ($row['Key'] === 'PRI') {
  471.         $this->primaryKey = $row['Field'];
  472.       }
  473.     }
  474.   }
  475.  
  476.   protected function createEntity(array $row) {
  477.     return new $this->entityClass($row);
  478.   }
  479.  
  480.   protected function createEntities(array $rows) {
  481.     return array_map([$this, 'createEntity'], $rows);
  482.   }
  483. }
  484.  
  485.  
  486. class MapperRepository {
  487.  
  488.   protected $db;
  489.   protected $mappers = [];
  490.  
  491.   public function __construct(DB $db) {
  492.     $this->db = $db;
  493.   }
  494.  
  495.   public function getDB() {
  496.     return $this->db;
  497.   }
  498.  
  499.   public function getMapper($name) {
  500.     if (!isset($this->mappers[$name])) {
  501.       $this->mappers[$name] = new $name($this->db);
  502.     }
  503.     return $this->mappers[$name];
  504.   }
  505. }
  506.  
  507.  
  508. class User extends Entity {
  509.   // Add some methods
  510. }
  511.  
  512.  
  513. class UserMapper extends EntityMapper {
  514.   protected $table = 'users';
  515.   protected $entityClass = 'User';
  516. }
  517.  
  518. /*
  519.  
  520. CREATE TABLE `users` (
  521.  `id` int(11) NOT NULL AUTO_INCREMENT,
  522.  `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  523.  `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  524.  `password` varchar(20) NOT NULL,
  525.  PRIMARY KEY (`id`),
  526.  UNIQUE KEY `username` (`username`),
  527.  UNIQUE KEY `email` (`email`)
  528. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  529.  
  530. */
  531.  
  532. // dump($_SERVER);
  533.  
  534. $db = new DB('test');
  535. $repo = new MapperRepository($db);
  536.  
  537. $user = new User([
  538.   'username' => 'u' . uniqid(),
  539.   'email' => uniqid() . '@example.com',
  540.   'password' => substr(md5(uniqid()), 0, 6)
  541. ]);
  542.  
  543. $map = $repo->getMapper('UserMapper');
  544. $map->save($user);
  545.  
  546. $users = $map->findAll([], 10, 20, ['id' => 'asc']);
  547.  
  548. dump($users);
  549.  
  550. dump($db->getQueries());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement