<?php
try {
throw new Exception("Some error message");
} catch(Exception $e) {
echo $e->getMessage();
}
?>
Category: PHP
preg_match
郵便番号:/^[0-9]{3}-[0-9]{4}$/
電話番後:/^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$/
E-mailアドレス:|^[0-9a-z_./?-]+@([0-9a-z-]+\.)+[0-9a-z-]+$|
filter_var(), FILTER_VALIDATE_EMAIL
The filter_var() function filters a variable with the specified filter.
Returns the filtered data on success, or FALSE on failure.
The FILTER_VALIDATE_EMAIL filter validates an e-mail address.
<?php
$email = "john.doe@example.com";
if (!filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
echo("$email is a valid email address");
} else {
echo("$email is not a valid email address");
}
?>
phpでMySQLに接続する書き方
phpでMySQLに接続, sql文にてデータを挿入する書き方例
$stmt->executeにて実行しています。変数の$dbhはdbhandlerの略です。
try {
$dbh = new PDO('mysql:host='/DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASSWORD);
} else (PDOException $e){
echo $e->getMessage();
exit;
}
$stmt = $dbh->prepare("select * from users where instagram_user_id=:user_id limit 1");
$stmt->execute(array(":user_id"=>$json->user->id));
$user = $stmt->fetch();
if (empty($user)){
$stmt = $dbh->prepare("insert into users(instagram_user_id, instagram_user_name, instagram_profile_picture, instagram_access_token, created, modified) values (:user_id, :user_name, :profile_picture, :access_token, now(), now());");
$pramas = array(
":user_id" =>$json->user->id,
":user_name" =>$json->user->username,
":profile_picture" =>$json->user->profile_picture,
":access_token"=>$json->access_token
);
$stmt->execute($params);
データの取得
$stmt = $dbh->prepare("select * from users where id=:last_insert_id limit 1");
$stmt->execute(array(":last_insert_id"=>$dbh->lastInsertId()));
$user = $stmt->fetch();
ログイン機能の実装
下のキャプチャのように、controller, model, viewにファイルを分けて作成しています。サインインデータはMysqlに格納して、ログイン時に呼び出して、判別しています。また、PostとGetでトークンの正誤判定も行っています。

controller.php
<?php
namespace MyApp;
class Controller {
private $_errors;
private $_values;
public function __construct(){
if(!isset($_SESSION['token'])){
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16));
}
$this->_errors = new \stdClass();
$this->_values = new \stdClass();
}
protected function setValues($key, $value){
$this->_values->$key = $value;
}
public function getValues(){
return $this->_values;
}
protected function setErrors($key, $error){
$this->_errors->$key = $error;
}
public function getErrors($key){
return isset($this->_errors->$key) ? $this->_errors->$key : '';
}
protected function hasError(){
return !empty(get_object_vars($this->_errors));
}
protected function isLoggedIn() {
// $_SESSION['me']
return isset($_SESSION['me']) && !empty($_SESSION['me']);
}
public function me(){
return $this->isLoggedIn() ? $_SESSION['me'] : null;
}
}
投票システム (1日1回)
jqueryのclick functionでopacityを変更し、更にformのvalueにidを入れて、ボタンが押されたら、MySQLにIDを格納します。そして、Mysqlからsql文でデータを読み込んで、集計結果を表示させます。また、IPアドレス、ユーザーエージェント、データで、複数投票を制限しています。
-reference
bindValue:Binds a value to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement.
SQL INSERT INTO Statement:INSERT INTO table_name VALUES (value1,value2,value3,…);
SQLデータ取得:SELECT col_name1, col_name2, … FROM db_name.tbl_name;
SQL GROUP BY Statement:The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by one or more columns.
user agent:is software (a software agent) that is acting on behalf of a user.
index.php
<?php
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/Poll.php');
try {
$poll = new \MyApp\Poll();
} catch (Exception $e){
echo $e->getMessage();
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST'){
$poll->post();
}
$err = $poll->getError();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Poll</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<?php if (isset($err)) : ?>
<div class="error"><?= h($err); ?></div>
<?php endif; ?>
<h1>Which do you like best?</h1>
<form action="" method="post">
<div class="row">
<div class="box" id="box_0" data-id="0"></div>
<div class="box" id="box_1" data-id="1"></div>
<div class="box" id="box_2" data-id="2"></div>
<input type="hidden" id="answer" name="answer" value="">
<input type="hidden" name="token" value="<?= h($_SESSION['token']); ?>">
</div>
<div id="btn">Vote and See Results</div>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(function(){
'use strict';
$('.box').on('click', function(){
$('.box').removeClass('selected');
$(this).addClass('selected');
$('#answer').val($(this).data('id'));
});
$('#btn').on('click', function(){
if ($('#answer').val() === ''){
alert('Choose one!');
} else {
$('form').submit();
}
});
$('.error').fadeOut(3000);
});
</script>
</body>
</html>
Poll.php
<?php
namespace MyApp;
class Poll{
private $_db;
public function __construct(){
$this->_connectDB();
$this->_createToken();
}
private function _createToken(){
if(!isset($_SESSION['token'])){
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16));
}
}
private function _validateToken(){
if(
!isset($_SESSION['token']) ||
!isset($_POST['token']) ||
$_SESSION['token'] !== $_POST['token']
){
throw new \Exception('invalid token!');
}
}
public function post(){
try {
$this->_validateToken();
$this->_validateAnswer();
$this->_save();
//redirect to result.php
header('Location: http://' . $_SERVER['HTTP_HOST'] . '/result.php');
} catch(\Exception $e){
// set error
$_SESSION['err'] = $e->getMessage();
// ridirect to index.php
header('Location: http://' . $_SERVER['HTTP_HOST']);
}
exit;
}
public function getResults(){
$data = array_fill(0, 3, 0);
$sql = "select answer, count(id) as c from answers group by answer";
foreach ($this->_db->query($sql) as $row){
$data[$row['answer']] = (int)$row['c'];
}
return $data;
}
public function getError(){
$err = null;
if (isset($_SESSION['err'])){
$err = $_SESSION['err'];
unset($_SESSION['err']);
}
return $err;
}
private function _validateAnswer(){
// var_dump($_POST);
// exit;
if (
!isset($_POST['answer']) ||
!in_array($_POST['answer'], [0, 1, 2])
){
throw new \Exception('invalid answer!');
}
}
private function _save(){
$sql = "insert into answers
(answer, created, remote_addr, user_agent, answer_date)
values (:answer, now(), :remote_addr, :user_agent, now())";
$stmt = $this->_db->prepare($sql);
$stmt->bindValue(':answer', (int)$_POST['answer'], \PDO::PARAM_INT);
$stmt->bindValue(':remote_addr', $_SERVER['REMOTE_ADDR'], \PDO::PARAM_STR);
$stmt->bindValue(':user_agent', $_SERVER['HTTP_USER_AGENT'], \PDO::PARAM_STR);
try {
$stmt->execute();
} catch(\PDOException $e){
throw new \Exception('No more vote for today!');
}
// exit;
}
private function _connectDB(){
try {
$this->_db = new \PDO(DSN, DB_USERNAME, DB_PASSWORD);
$this->_db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} catch(\PDOException $e){
throw new \Exception('Failed to connect DB');
}
}
}
result.php
<?php
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/Poll.php');
try {
$poll = new \MyApp\Poll();
} catch (Exception $e){
echo $e->getMessage();
exit;
}
$results = $poll->getResults();
// var_dump($results);
// exit;
// $results = [
// 0 => 12,
// 1 => 32,
// 2 => 44
// ];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Poll Result</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Result ...</h1>
<div class="row">
<?php for($i = 0; $i < 3; $i++) : ?>
<div class="box" id="box_<?= h($i); ?>"><?= h($results[$i]); ?></div>
<?php endfor; ?>
</div>
<a href="/"><div id="btn">Go Back</div></a>
</body>
</html>
styles.css
body {
font-size: 16px;
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
}
h1 {
font-size: 22px;
margin: 30px 0;
}
.row {
margin-bottom: 15px;
}
.box {
width: 150px;
height: 150px;
cursor: pointer;
display: inline-block;
opacity: 0.5;
color: #fff;
font-size: 48px;
font-weight: bold;
line-height: 150px;
}
.box + .box {
margin-left: 10px;
}
#box_0 { background: url('photo_0.jpg'); }
#box_1 { background: url('photo_1.jpg'); }
#box_2 { background: url('photo_2.jpg'); }
.selected {
opacity: 1.0;
}
#btn {
display: inline-block;
width: 150px;
padding: 7px;
font-size: 14px;
cursor: pointer;
border-radius: 5px;
background: #00aaff;
box-shadow: 0 4px 0 #0088cc;
color: #fff;
}
#btn:hover{
opacity: 0.8;
}
.error{
background: orange;
padding: 7px;
color: #fff;
}
mySQL
create database poll_php; grant all on poll_php.* to dbuser@localhost identified by 'xxxx'; use poll_php create table answers ( id int not null auto_increment primary key, answer int not null, created datetime, remote_addr varchar(15), user_agent varchar(255), answer_date date, unique unique_answer(remote_addr, user_agent, answer_date) );
クイズアプリ
予め正しい答えを全て配列の0に入れておき、click functionで正しいか正誤判定しています。選択肢はシャッフルして表示し、最後に、正解率を表示させています。
reference
Autoloading Classes:The spl_autoload_register() function registers any number of autoloaders, enabling for classes and interfaces to be automatically loaded if they are currently not defined. By registering autoloaders, PHP is given a last chance to load the class or interface before it fails with an error.
index.php
<?php
require_once(__DIR__ . '/config.php');
$quiz = new MyApp\Quiz();
if(!$quiz->isFinished()) {
$data = $quiz->getCurrentQuiz();
shuffle($data['a']);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Intractive Art</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<?php if ($quiz->isFinished()) : ?>
<div id="container">
<div id="result">
your score ...
<div><?= h($quiz->getScore()); ?>%</div>
</div>
<a href=""><div id="btn">Replay</div></a>
</div>
<?php $quiz->reset(); ?>
<?php else : ?>
<div id="container">
<h1>Q. <?= h($data['q']); ?></h1>
<ul>
<?php foreach ($data['a'] as $a) : ?>
<li class="answer"><?= h($a); ?></li>
<!-- <li class="answer">A1</li>
<li class="answer">A2</li>
<li class="answer">A3</li> -->
<?php endforeach; ?>
</ul>
<div id="btn" class="disabled"><?= $quiz->isLast() ? 'Show Result' : 'Next Question'; ?></div>
<input type="hidden" id="token" value="<?= h($_SESSION['token']); ?>">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="quiz.js"></script>
<?php endif; ?>
</body>
</html>
autoload.php
<?php
spl_autoload_register(function($class){
$prefix = 'MyApp\\';
if (strpos($class, $prefix) === 0){
$className = substr($class, strlen($prefix));
$classFilePath = __DIR__ . '/' . $className . '.php';
if (file_exists($classFilePath)){
require $classFilePath;
} else {
echo 'No such class' . $className;
exit;
}
}
});
[/php]
_answer.php
[php]
<?php
require_once(__DIR__ . '/config.php');
$quiz = new MyApp\Quiz();
try{
$correctAnswer = $quiz->checkAnswer();
} catch (Exception $e) {
header($_SERVER['SERVER_PROTOCOL']. ' 403 Forbidden', true, 403);
echo $e->getMessage();
exit;
}
header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
'correct_answer' => $correctAnswer
]);
Token.php
<?php
require_once(__DIR__ . '/config.php');
$quiz = new MyApp\Quiz();
try{
$correctAnswer = $quiz->checkAnswer();
} catch (Exception $e) {
header($_SERVER['SERVER_PROTOCOL']. ' 403 Forbidden', true, 403);
echo $e->getMessage();
exit;
}
header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
'correct_answer' => $correctAnswer
]);
Quiz.php
<?php
namespace MyApp;
class Quiz{
private $_quizSet = [];
public function __construct(){
$this->_setup();
Token::create();
if(!isset($_SESSION['current_num'])){
$this->_initSession();
}
}
public function checkAnswer(){
Token::validate('token');
$correctAnswer = $this->_quizSet[$_SESSION['current_num']]['a'][0];
if (!isset($_POST['answer'])){
throw new \Exception('answer not set!');
}
if($correctAnswer === $_POST['answer']){
$_SESSION['correct_count']++;
}
$_SESSION['current_num']++;
return $correctAnswer;
}
public function isFinished(){
return count($this->_quizSet) === $_SESSION['current_num'];
}
public function getScore() {
return round($_SESSION['correct_count'] / count($this->_quizSet) * 100);
}
public function isLast(){
return count($this->_quizSet) === $_SESSION['current_num'] + 1;
}
public function reset(){
$this->_initSession();
// $_SESSION['current_num'] = 0;
// $_SESSION['correct_count'] = 0;
}
private function _initSession(){
$_SESSION['current_num'] = 0;
$_SESSION['correct_count'] = 0;
}
public function getCurrentQuiz() {
return $this->_quizSet[$_SESSION['current_num']];
}
private function _setup(){
$this->_quizSet[] = [
'q' => '1543年は、ある物が日本に伝えられた年でもある。それは何か?',
'a' => ['鉄砲', 'キリスト教', '貿易', '忍法']
];
$this->_quizSet[] = [
'q' => '海上交通の要地に作られた港町で有名なのはどこ?',
'a' => ['堺', '伊丹', '京都', '江戸']
];
$this->_quizSet[] = [
'q' => '1639年に鎖国は完成する。この年から、来航を禁止された国はどこ?',
'a' => ['ポルトガル', 'イギリス', '清', 'オランダ']
];
}
}
config.php
quiz.js
$(function() {
'use strict';
$('.answer').on('click', function() {
var $selected = $(this);
if ($selected.hasClass('correct') || $selected.hasClass('wrong')) {
return;
}
$selected.addClass('selected');
var answer = $selected.text();
$.post('/_answer.php', {
answer: answer,
token: $('#token').val()
}).done(function(res) {
$('.answer').each(function() {
if ($(this).text() === res.correct_answer) {
$(this).addClass('correct');
} else {
$(this).addClass('wrong');
}
});
// alert(res.correct_answer);
if (answer === res.correct_answer) {
// correct!
$selected.text(answer + ' ... CORRECT!');
} else {
// wrong!
$selected.text(answer + ' ... WRONG!');
}
$('#btn').removeClass('disabled');
});
});
$('#btn').on('click', function() {
if (!$(this).hasClass('disabled')) {
location.reload();
}
});
});
body {
font-size: 16px;
font-family: Arial, sans-serif;
}
#container {
width: 500px;
margin: 15px auto;
}
h1, ul > li {
border: 1px solid #ddd;
border-radius: 5px;
}
h1 {
padding: 10px;
height: 50px;
font-size: 18px;
margin: 0 0 10px;
}
ul {
list-style: none;
margin: 0 0 10px;
padding: 0;
}
ul > li {
margin-bottom: 7px;
padding: 7px 10px;
cursor: pointer;
}
#btn {
text-align: center;
float: right;
width: 100px;
padding: 7px;
color: #fff;
border-radius: 5px;
background: #00aaff;
box-shadow: 0 4px 0 #0088cc;
cursor: pointer;
font-size: 14px;
}
#btn.disabled {
opacity: 0.5;
}
.correct{
color: limegreen;
font-weight: bold;
}
.wrong {
color: #ddd;
}
.selected {
font-weight: bold;
}
#result {
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 15px;
text-align: center;
padding: 30px 0 50px;
}
#result > div {
font-size: 64px;
font-weight: bold;
margin-top: 30px;
}
todo app
mysqlと連携したtodoアプリです。削除に際して、チェックボックスはajaxを使っています。
reference
openssl_random_pseudo_bytes:Generate a pseudo-random string of bytes
<?php
session_start();
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/functions.php');
require_once(__DIR__ . '/Todo.php');
// get todos
$todoApp = new \MyApp\Todo();
$todos = $todoApp->getAll();
//var_dump($todos);
//exit;
// create table todos(
// id int not null auto_increment primary key,
// state tinyint(1) default 0,
// title text
// );
//
// insert into todos (state, title) values
// (0, 'todo 0'),
// (0, 'todo 1'),
// (1, 'todo 2');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Todo app</title>
<link rel="stylesheet" href="styles.css">
<style>
</style>
</head>
<body>
<div id="container">
<h1>Todos</h1>
<form action="" id="new_todo_form">
<input type="text" id="new_todo" placeholder="What needs to be done?">
</form>
<ul id="todos">
<?php foreach ($todos as $todo) : ?>
<li id="todo_<?= h($todo->id); ?>" data-id="<?= h($todo->id); ?>">
<input type="checkbox" class="update_todo" <?php if ($todo->state === '1') { echo 'checked'; } ?>>
<span class="todo_title <?php if ($todo->state === '1') { echo 'done'; } ?>"><?= h($todo->title); ?></span>
<div class="delete_todo">x</div>
</li>
<?php endforeach; ?>
<li id="todo_template" data-id="">
<input type="checkbox" class="update_todo">
<span class="todo_title"></span>
<div class="delete_todo">x</div>
</li>
</ul>
</div>
<input type="hidden" id="token" value="<?= h($_SESSION['token']); ?>">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="todo.js"></script>
</body>
</html>
$(function() {
'use strict';
$('#new_todo').focus();
// update
$('#todos').on('click', '.update_todo', function() {
// idを取得
var id = $(this).parents('li').data('id');
// ajax処理
$.post('_ajax.php', {
id: id,
mode: 'update',
token: $('#token').val()
}, function(res) {
if (res.state === '1') {
$('#todo_' + id).find('.todo_title').addClass('done');
} else {
$('#todo_' + id).find('.todo_title').removeClass('done');
}
})
});
// delete
$('#todos').on('click', '.delete_todo', function() {
// idを取得
var id = $(this).parents('li').data('id');
// ajax処理
if (confirm('are you sure?')){
$.post('_ajax.php', {
id: id,
mode: 'delete',
token: $('#token').val()
}, function() {
$('#todo_' + id).fadeOut(800);
});
}
});
// create
$('#new_todo_form').on('submit', function() {
// titleを取得
var title = $('#new_todo').val();
// ajax処理
$.post('_ajax.php', {
title: title,
mode: 'create',
token: $('#token').val()
}, function(res) {
// liを追加
var $li = $('#todo_template').clone();
$li
.attr('id', 'todo_' + res.id)
.data('id', res.id)
.find('.todo_title').text(title);
$('#todos').prepend($li.fadeIn());
$('#new_todo').val('').focus();
});
return false;
});
});
<?php
session_start();
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/functions.php');
require_once(__DIR__ . '/Todo.php');
$todoApp = new \MyApp\Todo();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
$res = $todoApp->post();
header('Content-Type: application/json');
echo json_encode($res);
exit;
} catch (Exception $e) {
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
echo $e->getMessage();
exit;
}
}
<?php
namespace MyApp;
class Todo {
private $_db;
public function __construct() {
$this->_createToken();
try {
$this->_db = new \PDO(DSN, DB_USERNAME, DB_PASSWORD);
$this->_db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $e) {
echo $e->getMessage();
exit;
}
}
private function _createToken(){
if (!isset($_SESSION['token'])){
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16));
}
}
public function getAll() {
$stmt = $this->_db->query("select * from todos order by id desc");
return $stmt->fetchAll(\PDO::FETCH_OBJ);
}
public function post() {
$this->_validateToken();
if (!isset($_POST['mode'])) {
throw new \Exception('mode not set!');
}
switch ($_POST['mode']) {
case 'update':
return $this->_update();
case 'create':
return $this->_create();
case 'delete':
return $this->_delete();
}
}
private function _validateToken(){
if (
!isset($_SESSION['token']) ||
!isset($_POST['token']) ||
$_SESSION['token'] !== $_POST['token']
) {
throw new \Exception('invalid token!');
}
}
private function _update() {
if (!isset($_POST['id'])) {
throw new \Exception('[update] id not set!');
}
$this->_db->beginTransaction();
$sql = sprintf("update todos set state = (state + 1) %% 2 where id = %d", $_POST['id']);
$stmt = $this->_db->prepare($sql);
$stmt->execute();
$sql = sprintf("select state from todos where id = %d", $_POST['id']);
$stmt = $this->_db->query($sql);
$state = $stmt->fetchColumn();
$this->_db->commit();
return [
'state' => $state
];
}
private function _create() {
if (!isset($_POST['title']) || $_POST['title'] === '') {
throw new \Exception('[create] title not set!');
}
$sql = "insert into todos (title) values (:title)";
$stmt = $this->_db->prepare($sql);
$stmt->execute([':title' => $_POST['title']]);
return [
'id' => $this->_db->lastInsertId()
];
}
private function _delete() {
if (!isset($_POST['id'])) {
throw new \Exception('[delete] id not set!');
}
$sql = sprintf("delete from todos where id = %d", $_POST['id']);
$stmt = $this->_db->prepare($sql);
$stmt->execute();
return [];
}
}
PHP カレンダー
カレンダーにはテーブルの中に、phpのdate関数フォーマットを使います。wはNumeric representation of the day of the weekです。
PHP Date:http://php.net/manual/en/function.date.php
-reference
DatePeriod class:A date period allows iteration over a set of dates and times, recurring at regular intervals, over a given period.
sprintf:Return a formatted string
var_dump: Dumps information about a variable
<?php
require 'Calendar.php';
function h($s){
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
$cal = new \MyApp\Calendar();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Intractive Art</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<style>
</style>
</head>
<body>
<table>
<thead>
<tr>
<th><a href="/?t=<?php echo h($cal->prev); ?>">«</a></th>
<th colspan="5"><a href=""><?php echo h($cal->yearMonth); ?></a></th>
<th><a href="/?t=<?php echo h($cal->next); ?>">»</a></th>
</tr>
</thead>
<tbody>
<tr>
<td>Sun</td>
<td>Mon</td>
<td>Tue</td>
<td>Wed</td>
<td>Thu</td>
<td>Fri</td>
<td>Sat</td>
</tr>
<?php $cal->show(); ?>
</tbody>
<tfoot>
<tr>
<th colspan="7"><a href="/">Today</a></th>
</tr>
</tfoot>
</table>
<script>
</script>
</body>
</html>
<?php
namespace MyApp;
class Calendar {
public $prev;
public $next;
public $yearMonth;
private $_thisMonth;
public function __construct(){
try {
if(!isset($_GET['t']) || !preg_match('/\A\d{4}-\d{2}\z/', $_GET['t'])){
throw new \Exception();
}
$this->_thisMonth = new \DateTime($_GET['t']);
} catch (\Exception $e){
$this->_thisMonth = new \DateTime('first day of this month');
}
$this->prev = $this->_createPrevLink();
$this->next = $this->_createNextLink();
$this->yearMonth = $this->_thisMonth->format('F Y');
}
private function _createPrevLink(){
$dt = clone $this->_thisMonth;
return $dt->modify('-1 month')->format('Y-m');
}
private function _createNextLink(){
$dt = clone $this->_thisMonth;
return $dt->modify('+1 month')->format('Y-m');
}
public function show() {
$tail = $this->_getTail();
$body = $this->_getBody();
$head = $this->_getHead();
$html = '<tr>' . $tail . $body . $head . '</tr>';
echo $html;
}
private function _getTail(){
$tail = '';
$lastDayOfPrevMonth = new \DateTime('last day of ' . $this->yearMonth . ' -1 month');
while($lastDayOfPrevMonth->format('w') < 6){
$tail = sprintf('<td class="gray">%d</td>', $lastDayOfPrevMonth->format('d')) .
$tail;
$lastDayOfPrevMonth->sub(new \DateInterval('P1D'));
}
return $tail;
}
private function _getBody(){
$body = '';
$period = new \DatePeriod(
new \DateTime('first day of' . $this->yearMonth),
new \DateInterval('P1D'),
new \DateTime('first day of ' . $this->yearMonth . ' +1 month')
);
$today = new \DateTime('today');
foreach($period as $day){
if ($day->format('w') === '0') { $body .= '</tr><tr>';}
$todayClass = ($day->format('Y-m-d') === $today->format('Y-m-d')) ? 'today' : '';
$body .= sprintf('<td class="youbi_%d %s">%d</td>', $day->format('w'),
$todayClass, $day->format('d'));
}
return $body;
}
private function _getHead() {
$head = '';
$firstDayOfNextMonth = new \DateTime('first day of ' . $this->yearMonth . ' +1 month');
while ($firstDayOfNextMonth->format('w') > 0) {
$head .= sprintf('<td class="gray">%d</td>', $firstDayOfNextMonth->format('d'));
$firstDayOfNextMonth->add(new \DateInterval('P1D'));
}
return $head;
}
}
// $yearMonth = $thisMonth->format('F Y');
body {
font-family: Arial, sans-serif;
font-size: 14px;
}
a {
text-decoration: none;
}
table {
margin: 15px auto;
border: 1px solid #ddd;
border-collapse: collapse;
}
th {
background: #eee;
}
th, td {
padding: 7px;
text-align: center;
}
.youbi_0{
color: red;
}
.youbi_6 {
color: blue;
}
.today {
font-weight: bold;
}
.gray {
color: #dedede;
}
functions, config

<?php
function connectDb(){
try {
return new PDO(DSN, DB_USER, DB_PASSWORD);
} catch (PDOException $e){
echo $e->getMessage();
exit;
}
}
function h($s){
return htmlspecialchars($s, ENT_QUOTES, "UTF-8");
}
function setToken(){
if (!isset($_SESSION['token'])){
$_SESSION['token'] = sha1(uniqid(mt_rand(), true));
}
}
function checkToken(){
if (empty($_POST['token']) || $_POST['token'] != $_SESSION['token']){
echo "不正な処理です!";
exit;
}
}
/*
create database contact_php;
grant all on contact_php.* to dbuser@localhost identified by 'xxxx';
use contact_php
create table entries (
id int not null auto_increment primary key,
name varchar(255),
email varchar(255),
memo text,
created datetime,
modified datetime
);
alter table entries add status enum('active', 'deleted') default 'active' after memo;
*/
define('DSN','mysql:host=localhost;dbname=contact_php');
define('DB_USER','dbuser');
define('DB_PASSWORD','xxxx');
define('SITE_URL','http://192.168.33.10:8000');
define('ADMIN_URL', SITE_URL.'/admin/');
error_reporting(E_ALL & ~E_NOTICE);
session_set_cookie_params(0, '');