php簡易掲示板

投稿内容はdatファイルに格納します。

<?php

$dataFile = 'bbs.dat';

// csrf
session_start();

function setToken(){
  $token = sha1(uniqid(mt_rand(), true));
  $_SESSION&#91;'token'&#93; = $token;
}
function checkToken(){
  if (empty($_SESSION&#91;'token'&#93;) || ($_SESSION&#91;'token'&#93; !=$_POST&#91;'token'&#93;)){
    echo "不正なpostが行われました!";
    exit;
  }
}

function h($s){
  return htmlspecialchars($s, ENT_QUOTES, 'utf-8');
}

if($_SERVER&#91;'REQUEST_METHOD'&#93; == 'POST' &&
  isset($_POST&#91;'message'&#93;) &&
  isset($_POST&#91;'user'&#93;)){
    checkToken();

  $message =trim($_POST&#91;'message'&#93;);
  $user = trim($_POST&#91;'user'&#93;);

 if($message !== ''){

   $user = ($user === '') ? 'ななしさん' : $user;

   $message = str_replace("\t", '', $message);
   $user = str_replace("\t", '', $user);
   $postedAt = date('Y-m-d H:i:s');

  $newData = $message . "\t" . $user . "\t" . $postedAt. "\n";

  $fp = fopen($dataFile, 'a');
  fwrite($fp, $newData);
  fclose($fp);
   }
} else {
  setToken();
}

$posts = file($dataFile, FILE_IGNORE_NEW_LINES);

$posts = array_reverse($posts);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>簡易掲示板</title>
</head>
<body>
  <h1>簡易掲示板</h1>
  <form action="" method="post">
    message: <input type="text" name="message">
    user: <input type="text" name="user">
    <input type="submit" value="投稿">
    <input type="hidden" name="token" value="<?php echo h($_SESSION&#91;'token'&#93;); ?>">
  </form>
  <h2>投稿一覧(<?php echo count($posts); ?>件)</h2>
  <ul>
    <?php if (count($posts)) : ?>
      <?php foreach ($posts as $post) : ?>
        <?php list($message, $user, $postedAt) =  explode("\t", $post); ?>
        <li><?php echo h($message); ?>(<?php echo h($user); ?>)-<?php echo h($postedAt); ?></li>
      <?php endforeach; ?>
       <li></li>
    <?php else : ?>
    <li>まだ投稿はありません。</li>
  <?php endif; ?>
  </ul>
</body>
</html>

%e7%84%a1%e9%a1%8c

GNU Emacsを触ってみよう

mac os10では標準でunixコマンドラインにインストールされているので確認してみましょう。

mac-no-MacBook-Air:~ mac$ emacs --version
GNU Emacs 22.1.1
Copyright (C) 2007 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.

emacsの起動は、emacs、終了はcommand+cです。
画面:
-window:画面
-buffer:タブ
-mode line:今開いているbufferの情報
-mini buffer:コマンドの情報
a

ファイルは、コマンドキーで、ctr-x, ctr-fで、ディレクトリが表示されるので、編集したいファイルを開きます。

b

移動は以下のコマンドでも動けます。
c-f
c-b
c-p
c-n
c-a
c-e

express blog system

expressで簡易ブログをつくります。

var bodyParser = require('body-parser');
var logger = require('morgan');
var express = require('express'),
 app = express()
 post = require('./routes/post');

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.methodOverride());

app.use(logger('dev'));
app.use(function(err, req, ers, next){
  res.send(err.message);
});

// csrf対策
app.use(express.cookieParser());
app.use(express.session({secret: '386424ggrsr'}));
app.use(express.csrf());
app.use(function(req, res, next){
  res.locals.csrftoken = req.csrfToken();
  next();
});

// routing
app.get('/', post.index);
app.get('/posts/:id([0-9]+)', post.show);
app.get('/posts/new', post.new);
app.post('/posts/create', post.create);
app.get('/posts/:id/edit', post.edit);
app.put('/posts/:id', post.update);
app.delete('/posts/:id', post.destroy);
/*


app.get('/new', function(req, res){
  res.render('new');
});
*/
 app.listen(3000);
 console.log("server starting...");

post.js

var bodyParser = require('body-parser');
var logger = require('morgan');
var express = require('express'),
 app = express()
 post = require('./routes/post');

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.methodOverride());

app.use(logger('dev'));
app.use(function(err, req, ers, next){
  res.send(err.message);
});

// csrf対策
app.use(express.cookieParser());
app.use(express.session({secret: '386424ggrsr'}));
app.use(express.csrf());
app.use(function(req, res, next){
  res.locals.csrftoken = req.csrfToken();
  next();
});

// routing
app.get('/', post.index);
app.get('/posts/:id([0-9]+)', post.show);
app.get('/posts/new', post.new);
app.post('/posts/create', post.create);
app.get('/posts/:id/edit', post.edit);
app.put('/posts/:id', post.update);
app.delete('/posts/:id', post.destroy);
/*


app.get('/new', function(req, res){
  res.render('new');
});
*/
 app.listen(3000);
 console.log("server starting...");

index.html

<% include ../header %>
<body>
 <h1>Posts</h1>
 <ul>
   <% for (var i = 0; i < posts.length; i++){%>

   <li><a href="/posts/<%= i %>"><%= posts&#91;i&#93;.title %></a></li>
   <li><a href="/posts/<%= i %>/edit">[Edit]</a></li>
   <form method="post" action="/posts/<%= i %>">
     <input type="submit" value="del">
     <input type="hidden" name="_csrf" value="<%= csrftoken %>">
       <input type="hidden" name="_method" value="delete">
       <input type="hidden" name="id" value="<%= i %>">
   </form>
 </li>
   <% } %>
 </ul>
 <p><a href="/posts/">Add new</a></p>
<% include ../footer %>

post

var posts = [
  {title: 'title0', body: 'body0'},
  {title: 'title1', body: 'body1'},
  {title: 'title2', body: 'body2'},
];

exports.index = function(req, res){
  res.render('posts/index', {posts: posts});
};
exports.show = function(req, res){
  res.render('posts/show', {post: posts[req.params.id]});
};
exports.update = function(req, res, next){
  if (req.body.id !== req.params.id){
    next(new Error('ID not valid'));
  }else {
  posts[req.body.id] = {
    title: req.body.title,
    body: req.body.body
  };
  res.redirect('/');
};
exports.destroy = function(req, res, next){
  if (req.body.id !== req.params.id){
    next(new Error('ID not valid'));
  }else {
  posts.splice(req.body.id, 1);
  res.redirect('/');
}
};
exports.show = function(req, res){
  res.render('posts/new');
};
exports.create = function(req, res){
  var post = {
    title: req.body.title,
    body: req.body.body
  };
  posts.push(post);
  res.redirect('/');
};
exports.edit = function(req, res){
  res.render('posts/edit', {post: posts[req.params.id], id: req.params.id});
};

edit

<% include ../header %>
<body>
 <h1>Edit</h1>
<form method="post" action="/posts/<%= id %>">
  <input type="text" name="title" value="<%= post.title %>">
  <input type="text" name="body" value="<%= post.body %>">
  <input type="hidden" name="_csrf" value="<%= csrftoken %>">
  <input type="hidden" name="_method" value="Update">
  <input type="hidden" name="id" value="<%= id %>">
  <input type="submit" value="Update">
</form>

<p><a href="/">go back</a></p>
<% include ../footer %>

new

<% include ../header %>
<body>
 <h1>Add nnew</h1>
<form method="post" action="/posts/create">
  <input type="text" name="body">
  <input type="hidden" name="_csrf" value="<%= csrftoken %>">
  <input type="submit" value="add!">
</form>

<p><a href="/">go back</a></p>
<% include ../footer %>

show

<% include ../header %>
<body>
 <h1><%= post.title %></h1>
<p><%= post.body%></p>
<p><a href="/">go back</a></p>
<% include ../footer %>

header

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>

footer

</body>
</html>

expressのgetとpost

expressはnode.jsのフレームワークです。npmでサーバー側にインストールして動かします。ここでは、getとpostの挙動をみましょう。

var bodyParser = require('body-parser');
var logger = require('morgan');
var express = require('express'),
 app = express();

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');


// middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use(logger('dev'));
app.use(express.static(__dirname + '/public'));

app.get('/new', function(req, res){
  res.render('new');
});
app.post('/create', function(req, res){
  res.send(req.body.name);
});

 app.listen(3000);
 console.log("server starting...");

new.ejs


[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
server starting...
GET /new 200 22.101 ms - 141
POST /create 200 1.023 ms - 5
GET /new 304 4.233 ms - -
POST /create 200 0.560 ms - 6

ブラウザです。
%e7%84%a1%e9%a1%8c

grunt

module.exports = function(grunt){
 // config
grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    less: {
      build1:{
        src: ['src/styles1.less','src/styles2.less'],
        dest: 'build/styles.css'
      }
    },

    csslint: {
      check: {
        src: '<%= less.build.dest %>'
      }
    }

    cssmin: {
      minmize: {
        options: {
          banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */'
        },
        files: {
          'build/styles.min.css' : '<%= less.build.dest %>'
        }
      }
    }

});

 // plugin
 grunt.loadNpmTasks('grunt-contrib-less');
 grunt.loadNpmTasks('grunt-contrib-csslint');
 grunt.loadNpmTasks('grunt-contrib-cssmin');

 // tasks
 grunt.registerTask('default', ['less', 'cssmin','csslint']);
}

LESS ミックスイン

LESSでは様々な使い方があります。

階層的な表現

header {
  font-size: 18px;
  a{
    color: red;
    &:hover {
      color: green;
    }
  }
}

.button {
  &-ok {
    color: green;
  }
  &-ng {
    color: red;
  }
}

ミックスイン。値を入れたいときは、引数を入力しましょう。

.my-mixin(@width:5px; @padding:7px){
  border: @width solid red;
  padding: @padding;
  margin: 7px;
}

h1 {
  font-size: 24px;
  .my-mixin;
}

h2 {
  font-size: 20px;
  .my-mixin(10px; 20px);
}

extend


.button {
  font-size: 14px;
  border: 1px solid black;
}
.button-ok {
  &:extend(.button);
  color: green;
}
.button-ng {
  &:extend(.button);
  color: red;
}

LESS CSS

LESSを使うと、CSSをプログラムのように変数や関数などを使ってCSSをシンプルに書くことができます。
LESS

node.jsで動かしますので、サーバーサイドの環境を整えましょう。

[vagrant@localhost ~]$ mkdir less_lessons
[vagrant@localhost ~]$ cd less_lessons
[vagrant@localhost less_lessons]$ node -v
v0.10.46
[vagrant@localhost less_lessons]$ npm -v
2.15.1
[vagrant@localhost less_lessons]$ sudo npm install -g less

lessからCSSを作るには、コマンド”lessc”となります。

[vagrant@localhost less_lessons]$ lessc -v
lessc 2.7.1 (Less Compiler)
[vagrant@localhost less_lessons]$ lessc styles.less styles.css

それでは、いくつか、記法をみてみましょう。

/*
*/

@h1-color: red;
@h2-color: darken(@h1-color, 10%);
@h1-size: 24px;
@h2-size: @h1-size - 4px;

@header: h;
@color: color;
@dir: "/img";

@{header}1 {
  font-size: @h1-size;
  @{color}: @h1-color;
}

@{header}2 {
  font-size: @h2-size;
  @{color}: @h2-color;
  background: url("@{dir}/bg.png")
}

CSSの値を変数として扱っています。

Google web fontで遊ぼう

Webページ閲覧時、通常ではパソコンにインストールされているフォントしかブラウザで表示することが出来ませんが、「Webフォント」を使用すると任意のフォントを表示することが出来ます。

Google web fontの公式サイト
Google Web font
%e7%84%a1%e9%a1%8c

右のメニューバーで、Serif, Sans Serif, Display, Handwriting, Monospaceや、sortなどで、好みを選べます。
また、詳細にのページに行くと、誰が開発したかや、利用状況などを閲覧できます。

Google fontはheadタグとCSSに記入するだけで、簡単に利用できます。
例えば、以下のように記述します。

<!DOCTYPE html>
<html lang="ja" ng-app>
<head>
    <meta charset="UTF-8">
    <title>web fontの練習</title>
    <link rel="stylesheet" type="text/css"
      href="https://fonts.googleapis.com/css?family=Coiny|Tangerine">
      <style>
      h1 {
        font-family: 'Coiny', serif;
      }
      p {
        font-family: 'Tangerine', serif;
      }
      </style>
</head>
<body>
    <h1>Hello!</h1>
    <p>World</p>
</body>
</html>

%e7%84%a1%e9%a1%8c

Chrom developer toolのnetworkパネル

ページのロードに時間がかかってイライラすることありますよね。
そんなときは、chrom developer toolでどのコードがどれくらい時間がかかっているか調べることができます。

Networkで見ると、Analyticsの処理に時間がかかっていることがわかります。
%e7%84%a1%e9%a1%8c

チャートの青い線はDomが構成された時間の事です。オレンジの線は、画像の読み込みやiframeなどです。

更に詳しく見ていくには、timelineで見ていきます。

ちなみに、DOMとは、Document Object Modelのことで、Chrom developer toolでDOMを操作しても、ファイルは変更されません。

ubuntu14.04(trusty)にDockerをインストール

vagrantのubuntu14.04(trusty)にDockerをインストールします。

まず、パッケージマネージャーをアップデートしておきます。

sudo apt-get update

そして、Dockerの最新を取得します。

wget -qO- https://get.docker.com/ | sh

文末にアラートが表示されるので、ユーザーをDockerグループに追加します。

If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker vagrant

Remember that you will have to log out and back in for this to take effect!
vagrant@vagrant-ubuntu-trusty-64:~$ sudo usermod -aG docker vagrant

では、Dockerが入っているか確認してみましょう。大丈夫そうですね。

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c04b14da8d14: Pull complete
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker --version
Docker version 1.12.3, build 6b644ec