Linux查找所有非UTF-8编码的文件或转换成UTF-8

一行Linux命令查找所有非UTF-8编码的文件

find . ! -iregex '.*\.svn.*' -type f -name '*.php' -exec bash -c "enca -L zh_CN {}|grep GB2312 > /dev/null && echo {}" \;

一条命令将他们都转换成UTF8编码

find . ! -iregex '.*\.svn.*' -type f -name '*.php' -exec bash -c "enca -L zh_CN {}  | grep GB2312 >/dev/null && enconv -L zh_CN -x UTF-8 {}" \;

为 PHP 内置 WebServer 提供目录和文件索引

PHP 5.x 开始提供了一个简单的内置 WebServer,方便大家在没有安装 Apache 的情况下调试 PHP 代码。

要启动这个内置 WebServer 很简单,但它不能显示目录和文件索引,稍微有点不方便。本文提供了一个简单的实现。

使用方法:

php -S localhost:8080 webindex.php

然后打开浏览器访问 localhost:8080 即可以当前目录为根目录,显示目录列表和文件列表,并且可以在子目录中跳转。

实际运行效果:
参考来源:https://github.com/JBlond/php-built-in-webserver-router-script
朋友编写的版本:https://gist.github.com/dualface/70b62fc30b076026d0a8c87385eedebe

<?php
date_default_timezone_set('UTC');
function getfilesize($path) {
    $size = ceil(filesize($path)) . '';
    return str_repeat(' ', 8 - strlen($size)) . $size . ' KB';
}
function printlog($status = 200) {
    $time = date('D M j H:i:s Y');
    $addr = $_SERVER['REMOTE_ADDR'];
    $port = $_SERVER['REMOTE_PORT'];
    $uri = $_SERVER['REQUEST_URI'];
    $log = sprintf("[%s] %s:%s [%s]: %s\n", $time, $addr, $port, $status, $uri);
    file_put_contents('php://stdout', $log);
}
function echo_h($t) {
    echo htmlspecialchars($t);
}
function dumpfile($path) {
    $name = htmlspecialchars(pathinfo($path, PATHINFO_BASENAME));
    echo <<<EOT
<!DOCTYPE html>
<html>
<head>
<title>{$name}</title>
<meta charset="utf-8" />
<style>
pre {
    font-size: 14px;
    width: 80em;
    white-space: pre-wrap;
    white-space: -moz-pre-wrap;
    white-space: -pre-wrap;
    white-space: -o-pre-wrap;
    word-wrap: break-word;
}
</style>
</head>
<body>
<pre>
EOT;
echo_h(file_get_contents($path));
echo <<<EOT
</pre>
</body>
</html>
EOT;
}
// start
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = $_SERVER['DOCUMENT_ROOT'] . $uri;
if (!file_exists($path)) {
    printlog(404);
    http_response_code(404);
    return false;
}
$dump_exts = array(
    'txt', 'text', 'md', 'mdown', 'markdown', 'json',
    'c', 'cpp', 'h', 'hpp', 'm', 'mm', 'lua', 'py'
);
if (!is_dir($path)) {
    $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
    if ($ext == '' || in_array($ext, $dump_exts)) {
        printlog();
        dumpfile($path);
        return true;
    } else {
        // let server handle file
        return false;
    }
}
$this_dir = substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/') + 1);
$dir = $_SERVER['DOCUMENT_ROOT'] . $this_dir;
if (!is_dir($dir)) {
    printlog(404);
    http_response_code(404);
    return false;
}
$folder = opendir($dir);
if (!readdir($folder)) {
    printlog(404);
    http_response_code(404);
    return false;
}
$files = array();
while ($file = readdir($folder)) {
    $base = $this_dir == '/' ? '' : $this_dir;
    $ext = pathinfo($file, PATHINFO_EXTENSION);
    $path = $dir . DIRECTORY_SEPARATOR . $file;
    $filesize = getfilesize($path);
    $time = date('d-M-Y H:i:s', filemtime($path));
    $is_dir = is_dir($path);
    if (substr($file, 0, 1) == '.') continue;
    $files[] = array(
        'name' => $is_dir ? $file . '/' : $file,
        'ext' => $ext,
        'size' => $filesize,
        'time' => $time,
        'is_dir' => $is_dir
    );
}
usort($files, function($a, $b) {
    if ($a['is_dir']) {
        if ($b['is_dir']) {
            return $a['name'] < $b['name'] ? -1 : 1;
        } else {
            return -1;
        }
    }
    if ($b['is_dir']) {
        return 1;
    }
    return $a['name'] < $b['name'] ? -1 : 1;
});
$name_len = 50;
?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo_h($this_dir); ?></title>
<meta charset="utf-8" />
</head>
<body>
<h1>Index of <?php echo_h($this_dir); ?></h1>
<pre>
    Last modified               Size  Name
<hr /><?php if ($this_dir != '/'): ?>
[D] <a href="..">Parent Directory</a>
<?php
endif;
foreach ($files as $file):
    $dirflag = $file['is_dir'] ? '[D]' : '   ';
    $prefix = sprintf('%s %s %s', $dirflag, $file['time'], $file['size']);
    $name = $file['name'];
?>
<?php echo $prefix; ?>  <a href="<?php echo_h($name); ?>"><?php echo_h($name); ?></a>
<?php endforeach; ?>
</pre>
</body>
</html>

简化PHP命令行下接收参数

命令行下执行某个PHP脚本已是家常菜,大家也知道使用getopt接收参数(例子参考手册),但是它有一个缺点,必须先定义好需要接收哪些参数,并不能像$_GET/$_POST这么简单。
花了一些时间,写了一个类,让命令行参数像$_GET一样简单,随传随用。

class Options
{
    public static function get(string $name = '', $default = null)
    {
        static $argv = null;
        if (is_null($argv)) {
            [$shortopts, $longopts] = self::parse();
            $argv                   = getopt($shortopts, $longopts);
        }
        if ('' == $name) {
            return $argv;
        }
        return $argv[$name] ?? $default;
    }

    private static function parse(): array
    {
        if (empty($_SERVER['argv'])) {
            return [];
        }
        $opts = ['', []];
        foreach ($_SERVER['argv'] as $argv) {
            if (preg_match('/^\-\-([\w\-]+)/', $argv, $matches)) {
                $opts[1][] = $matches[1] . '::';
            } elseif (preg_match('/^\-([a-z])/', $argv, $matches)) {
                $opts[0] .= $matches[1] . '::';
            }
        }
        return $opts;
    }
}
php test.php --a=1 --b=2 -c3 -d4 -d5

全部接收

$options = Options::get();
var_dump($options);
/**
array(4) {
  ["a"]=>
  string(1) "1"
  ["b"]=>
  string(1) "2"
  ["c"]=>
  string(1) "3"
  ["d"]=>
  array(2) {
    [0]=>
    string(1) "4"
    [1]=>
    string(1) "5"
  }
}
*/

单个接收

echo Options::get('a');
// 1

使用默认值

echo Options::get('e', 1);
// 1

———————– 割 ———————–
在群里抛砖引玉,果然大牛们放出其他不错的方案。

【老王】环境变量的思路

AAAA=1111 php test.php
// 可得到 $_SERVER['AAAA'] = 1111

【supermoon】

php test.php "a=1&b=2"
// 结合parse_str()

nginx中配置跨域支持功能

在nginx.conf中配置

http {
  ......
  add_header Access-Control-Allow-Origin *;
  add_header Access-Control-Allow-Headers X-Requested-With;
  add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
  ......
}

这样就可以实现GET,POST,OPTIONS的跨域请求的支持
也可以

add_header Access-Control-Allow-Origin http://test.51testing.com;

指定允许的url。
或者动态指定

if ($http_origin ~* ".*\.example\.com") {
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Credentials true;
}

配置项详细解释:w3c-Cross-Origin Resource Sharing

深入剖析PHP输入流 php://input与POST/GET的区别

PHP输入流php://input
在使用xml-rpc的时候,server端获取client数据,主要是通过php输入流input,而不是$_POST数组。所以,这里主要探讨php输入流php://input
对于php://input介绍,PHP官方手册文档有一段话对它进行了很明确地概述:

“php://input allows you to read raw POST data. It is a less memory intensive alternative to $HTTP_RAW_POST_DATA and does not need any special php.ini directives. php://input is not available with enctype=”multipart/form-data”.

继续阅读深入剖析PHP输入流 php://input与POST/GET的区别

Call to undefined function imagettftext()解决方法

在一个新环境中装Tipask v2.5的时候发现后台验证码无法显示。出错的函数是imagettftext(),由于index.php使用了error_reporting(0)将错误隐去,导致这次莫名的错误,去掉,错误立马出现:

Fatal error: Call to undefined function imagettftext()

现在我们就明确了,出现错误的原因是PHP编译时没有加上FreeType。
继续阅读Call to undefined function imagettftext()解决方法

一个快速获取/更新 Let’s encrypt 证书的脚本

调用 acme_tiny.py 认证、获取、更新证书,不需要额外的依赖。

下载到本地

# wget https://raw.githubusercontent.com/xdtianyu/scripts/master/lets-encrypt/letsencrypt.conf
# wget https://raw.githubusercontent.com/xdtianyu/scripts/master/lets-encrypt/letsencrypt.sh
wget https://raw.githubusercontent.com/carpliyz/Lets-encrypt/master/letsencrypt.conf
wget https://raw.githubusercontent.com/carpliyz/Lets-encrypt/master/letsencrypt.sh
chmod +x letsencrypt.sh

配置文件

只需要修改 DOMAIN_KEY DOMAIN_DIR DOMAINS 为你自己的信息

ACCOUNT_KEY=”letsencrypt-account.key”
DOMAIN_KEY=”example.com.key”
DOMAIN_DIR=”/var/www/example.com”
DOMAINS=”DNS:example.com,DNS:whatever.example.com”

执行过程中会自动生成需要的 key 文件。

运行

./letsencrypt.sh letsencrypt.conf

注意

需要已经绑定域名到 /var/www/example.com 目录,即通过 http://example.com http://whatever.example.com 可以访问到 /var/www/example.com 目录,用于域名的验证

看到以下信息表示生成/更新成功

Generate CSR…
Parsing account key…
Parsing CSR…
Registering account…
Already registered!
Verifying www.hdj.me…
www.hdj.me verified!
Signing certificate…
Certificate signed!
New cert: www.chained.crt has been generated

生成证书包括

-rw-r–r– 1 root root 3.2K Dec 28 17:04 letsencrypt-account.key
-rw-r–r– 1 root root 192 Dec 28 17:03 letsencrypt.conf
-rwxr-xr-x 1 root root 1.7K Dec 28 16:57 letsencrypt.sh
-rw-r–r– 1 root root 1.7K Dec 24 00:58 lets-encrypt-x1-cross-signed.pem
-rw-r–r– 1 root root 3.4K Dec 29 08:38 www.chained.crt
-rw-r–r– 1 root root 1.8K Dec 29 08:38 www.crt
-rw-r–r– 1 root root 920 Dec 29 08:37 www.csr
-rw-r–r– 1 root root 1.7K Dec 28 17:04 example.com.key

nginx配置

listen 443 ssl;
ssl_certificate /path/letsencrypt/www.chained.crt;
ssl_certificate_key /path/letsencrypt/example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

cron 定时任务

每个月自动更新一次证书,可以在脚本最后加入 service nginx reload等重新加载服务。

0 0 1 * * /etc/nginx/certs/letsencrypt.sh /etc/nginx/certs/letsencrypt.conf >> /var/log/lets-encrypt.log 2>&1

Let’s Encrypt 试用

Let's Encrypt

听说Let’s Encrypt已经开始Public Beta了,于是马上开始试用。Let’s Encrypt 是一个新的数字证书认证机构,它通过自动化的过程消除创建和安装证书的复杂性,为网站提供免费的 SSL/TLS 证书。

以下是使用 Let’s Encrypt 的过程:

获取客户端并执行

-- 注意python版本要求>=2.7
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory auth

一、选择认证方式

在安装一些依赖包后,Let’s Encrypt 将弹出 TUI 界面要求选择认证的方式:手动或独立。这里为了省事,选择独立认证。

二、接着输入 Email 地址

三、同意许可协议

四、输入域名

在此,输入 hdj.me 和 www.hdj.me,多个域名使用逗号或空格分隔。

也可以选择命令行模式:

./letsencrypt-auto certonly -a manual --debug -d www.hdj.me

五、完成

当看到下列消息时,说明认证已经成功完成:

– Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/linuxtoy.org/fullchain.pem. Your cert will
expire on 2016-01-25. To obtain a new version of the certificate in
the future, simply run Let’s Encrypt again.

Let’s Encrypt 将认证的信息保存于 /etc/letsencrypt 目录。

然后,在 NGINX 的配置文件中将下面两行设置成 Let’s Encrypt 的实际路径即可:

ssl_certificate /etc/letsencrypt/live/www.hdj.me/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.hdj.me/privkey.pem;

上一张预览图:
QQ截图20151216182945

值得注意的是:
目前 Let’s Encrypt 的证书有效期为 90 天,之后需要手动续期。另外,在请求证书认证时会有频率限制。总的来说,证书的认证过程还是非常容易的,而且又是免费,所以对此有需要的朋友不妨一试。

重定向简单么?不,它很复杂!

重定向是一个看似简单,实际很复杂的问题。HTTP状态码一共才五个系列(1XX,2XX,3XX,4XX,5XX),而重定向状态码被单独作为一个系列(3XX)存在,足以说明它的重要性,但是很多人认为知道301/302的区别就算熟悉重定向了,这实在是一大谬误。本文主要介绍一下303/307。

为了方便测试,首先熟悉一下如何使用curl命令得到响应头:

-I/–head
(HTTP/FTP/FILE) Fetch the HTTP-header only! HTTP-servers feature the command HEAD which this uses to get nothing but the header of a document. When used on a FTP or FILE file, curl displays the file size and last modification time only.

例子命令:curl -I http://www.google.com/

通常PHP里的重定向是这样的:

header('Location: http://localhost/');
exit();

通过curl命令访问如上的代码,我们将得到如下响应头:

HTTP/1.1 302 Found
Location: http://localhost/

如上的重定向代码在编程里很常用,比如说添加文章成功后,跳转回列表页,不过这里的重定向状态码302却是值得商榷的。

这还得从头说起,在HTTP1.0的时代,那时候302的名字还是“Moved Temporarily”,但在实际使用上,302往往包含了两方面的意思(也就是后来的303/307),为了消除可能的混淆,在HTTP1.1中,302被重命名为“Found”,并新加了303(See Other)和307(Temporary Redirect),至于PHP之所以在重定向时缺省使用302状态码是为了兼容的目的,所以不到不得已(有时候,客户端是HTTP1.0的,只理解302),不应该使用302。

303和307都把重定向的URI置于Location头中,他们的区别在于:

  • 303:对于POST请求,它表示请求已经被处理,客户端可以接着使用GET方法去请求Location里的URI。
  • 307:对于POST请求,表示请求还没有被处理,客户端应该向Location里的URI重新发起POST请求。

下面看看PHP如何发送非302的重定向,以303为例:

// 第一种方法
header('Location: http://localhost/', true, 303);

// 第二种方法
header('HTTP/1.1 303 See Other');
header('Location: http://localhost/');

使用curl命令,你就会看到如下响应头:

HTTP/1.1 303 See Other
Location: http://localhost/

总结,本文说的主要是303/307之间的关系。之所以明确区分是为了让状态码本身能够准确的表达响应的含义,从而尽可能的避免对重定向的滥用。

原文地址:http://blog.163.com/wangbo_tester/blog/static/128067921200981741639682/
参考地址:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html