Advertisement
Guest User

Untitled

a guest
Jul 2nd, 2017
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 12.02 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 array_get($array, $key, $default = null) {
  20.   return array_key_exists($key, $array) ? $array[$key] : $default;
  21. }
  22.  
  23.  
  24. function registry($k, $v = null) {
  25.   static $r = [];
  26.   return func_num_args() > 1 ? $r[$k] = $v : array_get($r, $k);
  27. }
  28.  
  29.  
  30. function template($filename, $data = []) {
  31.   extract($data);
  32.   ob_start();
  33.   require TEMPLATE_PATH . DS . $filename . '.php';
  34.   return ob_end_clean();
  35. }
  36.  
  37.  
  38. class Set implements Countable, IteratorAggregate {
  39.  
  40.   protected $data = [];
  41.  
  42.   public function __construct($data = []) {
  43.     $this->update($data);
  44.   }
  45.  
  46.   public function has($v) {
  47.     return in_array($v, $this->data, true);
  48.   }
  49.  
  50.   public function add($v) {
  51.     if (!$this->has($v)) {
  52.       $this->data[] = $v;
  53.     }
  54.   }
  55.  
  56.   public function update(array $data) {
  57.     foreach ($data as $v) {
  58.       $this->add($v);
  59.     }
  60.   }
  61.  
  62.   public function remove($v) {
  63.     $i = array_search($v, $this->data, true);
  64.     if (false !== $i) {
  65.       unset($this->data[$i]);
  66.       // Можно закомментировать, если не нужны индексы, идущие по порядку
  67.       $this->reindex();
  68.     }
  69.   }
  70.  
  71.   public function clear() {
  72.     $this->data = [];
  73.   }
  74.  
  75.   public function toArray() {
  76.     return $this->data;
  77.   }
  78.  
  79.   public function count() {
  80.     return count($this->data);
  81.   }
  82.  
  83.   public function getIterator() {
  84.     $obj = new ArrayObject($this->data);
  85.     return $obj->getIterator();
  86.   }
  87.  
  88.   protected function reindex() {
  89.     $this->data = array_values($this->data);
  90.   }
  91. }
  92.  
  93.  
  94. // ...
  95.  
  96.  
  97. interface AdapterInterface {
  98.  
  99.   public function connect();
  100.  
  101.   public function disconnect();
  102.  
  103.   public function beginTransaction();
  104.  
  105.   public function commit();
  106.  
  107.   public function rollBack();
  108.  
  109.   public function query($sql, $bind = []);
  110.  
  111.   public function exec($sql, $bind = []);
  112.  
  113.   public function lastInsertId();
  114.  
  115.   public function quoteIdentifier($name);
  116.  
  117.   public function find($table, $id, $key = 'id');
  118.  
  119.   public function findAll($table);
  120.  
  121.   public function insert($table, array $data);
  122.  
  123.   public function update($table, array $data, $id, $key = 'id');
  124.  
  125.   public function delete($table, $id, $key = 'id');
  126.  
  127.   public function describe($table);
  128. }
  129.  
  130.  
  131. interface ResultInterface {
  132.  
  133.   public function fetch();
  134.  
  135.   public function fetchAll();
  136.  
  137.   public function fetchColumn();
  138.  
  139.   public function affectedRows();
  140. }
  141.  
  142.  
  143. class DBException extends Exception {}
  144.  
  145.  
  146. class MySQLAdapter implements AdapterInterface {
  147.  
  148.   protected $pdo;
  149.   protected $hostname = 'localhost';
  150.   protected $port = 3306;
  151.   protected $user = 'root';
  152.   protected $password;
  153.   protected $dbname;
  154.   protected $charset = 'utf8';
  155.  
  156.   public function __construct(array $options) {
  157.     $this->hostname = array_get($options, 'hostname', $this->hostname);
  158.     $this->port = array_get($options, 'port', $this->port);
  159.     $this->user = array_get($options, 'user', $this->user);
  160.     $this->password = array_get($options, 'password', $this->password);
  161.     $this->dbname = array_get($options, 'dbname', $this->dbname);
  162.     $this->charset = array_get($options, 'charset', $this->charset);
  163.     $this->connect();
  164.   }
  165.  
  166.   public function connect() {
  167.     $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s',
  168.       $this->hostname,
  169.       $this->port,
  170.       $this->dbname,
  171.       $this->charset
  172.     );
  173.     $this->pdo = new PDO(
  174.       $dsn,
  175.       $this->user,
  176.       $this->password,
  177.       [
  178.         PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  179.         PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
  180.       ]
  181.     );
  182.   }
  183.  
  184.   public function disconnect() {
  185.     $this->pdo = null;
  186.   }
  187.  
  188.   public function __destruct() {
  189.     $this->disconnect();
  190.   }
  191.  
  192.   public function beginTransaction() {
  193.     return $this->pdo->beginTransaction();
  194.   }
  195.  
  196.   public function commit() {
  197.     return $this->pdo->commit();
  198.   }
  199.  
  200.   public function rollBack() {
  201.     return $this->pdo->rollBack();
  202.   }
  203.  
  204.   public function query($sql, $bind = []) {
  205.     $s = $this->pdo->prepare($sql);
  206.     $s->execute($bind);
  207.     return new MySQlResult($s);
  208.   }
  209.  
  210.   // Возвращает количество модифицированных запросом рядов
  211.   public function exec($sql, $bind = []) {
  212.     return $this->query($sql, $bind)->affectedRows();
  213.   }
  214.  
  215.   public function lastInsertId() {
  216.     return $this->pdo->lastInsertId();
  217.   }
  218.  
  219.   public function quoteIdentifier($name) {
  220.     return '`' . str_replace('`', '``', $name) . '`';
  221.   }
  222.  
  223.   public function find($table, $id, $key = 'id') {
  224.     $sql = sprintf(
  225.       'SELECT * FROM %s WHERE %s = ?;',
  226.       $this->quoteIdentifier($table),
  227.       $this->quoteIdentifier($key)
  228.     );
  229.     writeln($sql);
  230.     return $this->query($sql, [$id])->fetch();
  231.   }
  232.  
  233.   public function findAll($table) {
  234.     $sql = sprintf(
  235.       'SELECT * FROM %s;',
  236.       $this->quoteIdentifier($table)
  237.     );
  238.     writeln($sql);
  239.     return $this->query($sql)->fetchAll();
  240.   }
  241.  
  242.   public function insert($table, array $data) {
  243.     $sql = sprintf(
  244.       'INSERT INTO %s (%s) VALUES (%s);',
  245.       $this->quoteIdentifier($table),
  246.       implode(', ', array_map([$this, 'quoteIdentifier'], array_keys($data))),
  247.       substr(str_repeat('?, ', count($data)), 0, -2)
  248.     );
  249.     writeln($sql);
  250.     $affected_rows = $this->exec($sql, array_values($data));
  251.     return $affected_rows ? $this->lastInsertId() : false;
  252.   }
  253.  
  254.   public function update($table, array $data, $id, $key = 'id') {
  255.     $set = [];
  256.     foreach (array_keys($data) as $column) {
  257.       $set[] = sprintf('%s = ?', $this->quoteIdentifier($column));
  258.     }
  259.     $bind = array_values($data);
  260.     $bind[] = $id;
  261.     $sql = sprintf(
  262.       'UPDATE %s SET %s WHERE %s = ?;',
  263.       $this->quoteIdentifier($table),
  264.       implode(', ', $set),
  265.       $this->quoteIdentifier($key)
  266.     );
  267.     writeln($sql);
  268.     return $this->exec($sql, $bind);
  269.   }
  270.  
  271.   public function delete($table, $id, $key = 'id') {
  272.     $sql = sprintf(
  273.       'DELETE FROM %s WHERE %s = ?;',
  274.       $this->quoteIdentifier($table),
  275.       $this->quoteIdentifier($key)
  276.     );
  277.     writeln($sql);
  278.     return $this->exec($sql, [$id]);
  279.   }
  280.  
  281.   public function describe($table) {
  282.     $sql = sprintf(
  283.       'DESCRIBE %s;',
  284.       $this->quoteIdentifier($table)
  285.     );
  286.     return $this->query($sql)->fetchAll();
  287.   }
  288. }
  289.  
  290.  
  291. class MySQlResult implements ResultInterface {
  292.  
  293.   protected $statement;
  294.  
  295.   public function __construct(PDOStatement $statement) {
  296.     $this->statement = $statement;
  297.   }
  298.  
  299.   public function fetch() {
  300.     return $this->statement->fetch();
  301.   }
  302.  
  303.   public function fetchAll() {
  304.     return $this->statement->fetchAll();
  305.   }
  306.  
  307.   public function fetchColumn() {
  308.     return $this->statement->fetchColumn();
  309.   }
  310.  
  311.   public function affectedRows() {
  312.     return $this->statement->rowCount();
  313.   }
  314. }
  315.  
  316.  
  317. // Ведет себя как объект и как массив.
  318. // http://ideone.com/2j2Dwi
  319. // $m->data возвращвет null
  320. abstract class AbstractModel implements ArrayAccess, Countable,
  321.     IteratorAggregate {
  322.  
  323.   protected $data;
  324.  
  325.   public function __construct(array $data = []) {
  326.     $this->data = $data;
  327.   }
  328.  
  329.   public function toArray() {
  330.     return $this->data;
  331.   }
  332.  
  333.   public function set($key, $value) {
  334.     $this->data[$key] = $value;
  335.   }
  336.  
  337.   public function has($key) {
  338.     return array_key_exists($key, $this->data);
  339.   }
  340.  
  341.   public function get($key, $default = null) {
  342.     return $this->has($key) ? $this->data[$key] : $default;
  343.   }
  344.  
  345.   public function remove($key) {
  346.     unset($this->data[$key]);
  347.   }
  348.  
  349.   public function keys() {
  350.     return array_keys($this->data);
  351.   }
  352.  
  353.   public function values() {
  354.     return array_values($this->data);
  355.   }
  356.  
  357.   public function __set($property, $value) {
  358.     $this->set($property, $value);
  359.   }
  360.  
  361.   public function __get($property) {
  362.     return $this->get($property);
  363.   }
  364.  
  365.   public function __isset($property) {
  366.     return $this->has($property);
  367.   }
  368.  
  369.   public function __unset($property) {
  370.     $this->remove($property);
  371.   }
  372.  
  373.   public function offsetSet($key, $value) {
  374.     // $obj[] = $value;
  375.     if (!is_null($key)) {
  376.       $this->set($key, $value);
  377.     }
  378.   }
  379.  
  380.   public function offsetExists($key) {
  381.     return $this->has($key);
  382.   }
  383.  
  384.   public function offsetUnset($key) {
  385.     $this->remove($key);
  386.   }
  387.  
  388.   public function offsetGet($key) {
  389.     return $this->get($key);
  390.   }
  391.  
  392.   public function count() {
  393.     return count($this->data);
  394.   }
  395.  
  396.   public function getIterator() {
  397.     $obj = new ArrayObject($this->data);
  398.     return $obj->getIterator();
  399.   }
  400. }
  401.  
  402.  
  403. abstract class AbstractMapper {
  404.  
  405.   protected $adapter;
  406.   protected $primaryKey;
  407.   protected $fields = [];
  408.   // Эти свойства переопределяются в классах наследниках
  409.   protected $table;
  410.   protected $modelClass;
  411.  
  412.   public function __construct($adapter) {
  413.     $this->adapter = $adapter;
  414.     $this->describe();
  415.   }
  416.  
  417.   public function find($id) {
  418.     $row = $this->adapter->find($this->table, $id, $this->primaryKey);
  419.     return $row ? $this->createObject($row) : null;
  420.   }
  421.   public function findAll() {
  422.     $rows = $this->adapter->findAll($this->table);
  423.     return $this->createObjects($rows);
  424.   }
  425.  
  426.   public function save(AbstractModel $o) {
  427.     return $o->{$this->primaryKey} ? $this->update($o) : $this->insert($o);
  428.   }
  429.  
  430.   public function update(AbstractModel $o) {
  431.     $data = $this->getModelData($o);
  432.     $affected_rows = $this->adapter->update(
  433.       $this->table,
  434.       $data,
  435.       $o->{$this->primaryKey},
  436.       $this->primaryKey
  437.     );
  438.   }
  439.  
  440.   public function insert(AbstractModel $o) {
  441.     $data = $this->getModelData($o);
  442.     $id = $this->adapter->insert($this->table, $data);
  443.     return $o->{$this->primaryKey} = $id;
  444.   }
  445.  
  446.   public function remove(AbstractModel $o) {
  447.     $affected_rows = $this->adapter->delete(
  448.       $this->table,
  449.       $o->{$this->primaryKey},
  450.       $this->primaryKey
  451.     );
  452.     if ($affected_rows) {
  453.       $o->{$this->primaryKey} = null;
  454.       return true;
  455.     }
  456.     return false;
  457.   }
  458.  
  459.   protected function describe() {
  460.     $rows = $this->adapter->describe($this->table);
  461.     foreach ($rows as $row) {
  462.       $this->fields[] = $row['Field'];
  463.       if ($row['Key'] === 'PRI') {
  464.         $this->primaryKey = $row['Field'];
  465.       }
  466.     }
  467.   }
  468.  
  469.   protected function createObject($row) {
  470.     return new $this->modelClass($row);
  471.   }
  472.  
  473.   protected function createObjects($rows) {
  474.     return array_map([$this, 'createObject'], $rows);
  475.   }
  476.  
  477.   // TODO: придумать более подходящее имя
  478.   protected function getModelData(AbstractModel $o) {
  479.     $ret = [];
  480.     // Выбираем только те ключи, которые совпадают с именами колонок таблицы
  481.     $keys = array_intersect($this->fields, $o->keys());
  482.     foreach ($keys as $k) {
  483.       $ret[$k] = $o[$k];
  484.     }
  485.     return $ret;
  486.   }
  487. }
  488.  
  489.  
  490. class User extends AbstractModel {
  491.   // Add some methods
  492. }
  493.  
  494.  
  495. class UserMapper extends AbstractMapper {
  496.   protected $table = 'users';
  497.   protected $modelClass = 'User';
  498. }
  499.  
  500. /*
  501.  
  502. CREATE TABLE `users` (
  503.  `id` int(11) NOT NULL AUTO_INCREMENT,
  504.  `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  505.  `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  506.  `password` varchar(20) NOT NULL,
  507.  PRIMARY KEY (`id`),
  508.  UNIQUE KEY `username` (`username`),
  509.  UNIQUE KEY `email` (`email`)
  510. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  511.  
  512. */
  513.  
  514. $db = new MySQLAdapter(['dbname' => 'test']);
  515. $userMapper = new UserMapper($db);
  516.  
  517. $user = new User([
  518.   'username' => 'r00t',
  519.   'email' => 'root@localhost',
  520.   'password' => '1'
  521. ]);
  522.  
  523. $userMapper->save($user);
  524. dump($user);
  525. $user->password = '123456';
  526. $userMapper->save($user);
  527. dump($user);
  528. $userMapper->remove($user);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement