找到
177
篇与
技术教程
相关的结果
- 第 18 页
-
Typecho 1.2.0 提示版本更新Bug解决方法 Typecho以一向高雅的动作宣布了自己的回归: “这不是玩笑,我们回来了” Typecho 1.2.0 我也按照更新的方法将我的typecho更新到了1.2.0版,但是在后台却还是看到提示升级,当前版本1.2.0,最新版本1.2.0的字样。 图片 可以确定这是typecho的一个Bug,官方也给出了修复的办法,在代码的判断中修改一处就可以解决了。 if ( isset($json['release']) && preg_match("/^[0-9\.]+$/", $json['release']) && version_compare($json['release'], $version, '>=') && version_compare($json['release'], $version, '>') )在 var/Widget/Ajax.php 这个文件中,将 >= 改为 > 即可。 图片
-
PHP各种常用函数方法封装 当前日期和时间 /** * 当前日期和时间 * @return string */ function date_time(): string { return date('Y-m-d H:i:s'); }动态创建Element元素 /** * 动态创建Element元素 * * @param string $element * @return ElementBuilder */ function element($element) { return new ElementBuilder($element); } /** * @package ElementBuilder * @description 动态创建HTML的Element标签 * @author 易航 * @version 1.0 * @link http://blog.yihang.info */ class ElementBuilder { private $options = [ 'element' => 'div', 'inner' => null, 'attributes' => [] ]; private function attributes($attributes) { return FormBuilder::attributes($attributes); } public function __construct($element) { $this->options['element'] = $element; } /** * 设置标签内部的HTML内容 * @param $innerHTML 要设置的标签HTML内容 * @return $this */ public function innerHTML($innerHTML) { $this->options['inner'] = $innerHTML; return $this; } /** * 设置标签内部的文本内容 * @param $innerText 要设置的标签文本内容 * @return $this */ public function innerText($innerText) { $this->options['inner'] = empty($innerText) ? $innerText : htmlentities($innerText); return $this; } /** * 批量设置标签的属性 * @param $attributes * @return $this */ public function attr($attributes, $content = null) { if (is_array($attributes)) { foreach ($attributes as $key => $value) { $this->options['attributes'][$key] = $value; } } if (is_string($attributes)) { $this->options['attributes'][$attributes] = $content; } return $this; } /** * 获取生成的HTML内容 * @return string */ public function get($html = null) { $this->innerHTML($html); $element = $this->options['element']; $content = $this->options['inner']; $attributes = $this->attributes($this->options['attributes']); if ($content === false) { $html = "<$element$attributes />"; } else { $html = "<$element$attributes>$content</$element>"; } return $html; } public function __toString() { return $this->get($this->options['inner']); } }解压ZIP文件 function zipExtract($src, $dest) { // 通过ZipArchive的对象处理zip文件 $zip = new ZipArchive(); //新建一个ZipArchive的对象 /* $zip->open这个方法的参数表示处理的zip文件名。 如果对zip文件对象操作成功,$zip->open这个方法会返回TRUE */ if ($zip->open($src) === true) { $zip->extractTo($dest); //假设解压缩到$dest路径文件夹的子文件夹php $zip->close(); //关闭处理的zip文件 return true; } return false; }获取指定目录下的文件和目录列表 /** * 获取指定目录下的文件和目录列表 * * @param string $directory 要扫描的目录 * @return DirScanner */ function scan_dir(string $directory) { return new DirScanner($directory); } /** * 获取指定目录下的文件和目录列表 */ class DirScanner { private array $list; private string $directory; /** * @param string $directory 要扫描的目录 */ public function __construct(string $directory) { $this->directory = str_replace(['//', '\\\\'], ['/', '\\'], $directory); $this->directory = preg_match('/^\/|\\\$/', $this->directory) ? $this->directory : $this->directory . DIRECTORY_SEPARATOR; $this->list = array_values(array_diff(scandir($directory), ['.', '..'])); } /** * 只获取文件 */ public function files(array $suffixes = []): array { return $this->filter($suffixes, 'is_file'); } /** * 只获取目录 */ public function dirs(): array { return $this->filter([], 'is_dir'); } /** * 获取所有文件和目录 */ public function all(): array { return $this->list; } /** * 过滤列表 */ private function filter(array $suffixes, callable $callback): array { $directory = $this->directory; return array_filter($this->list, function ($item) use ($directory, $suffixes, $callback) { $path = $directory . $item; if (!empty($suffixes) && is_file($path)) { $extension = pathinfo($item, PATHINFO_EXTENSION); if (!in_array($extension, $suffixes)) return false; } return $callback($path); }); } }删除文件或文件夹 /** * 删除文件或文件夹 * * @param string $directory 目录的路径。 * @param resource $context 上下文流(context stream) resource * @return bool */ function delete_directory(string $directory, $context = null) { if (is_dir($directory)) { $objects = scandir($directory); foreach ($objects as $object) { if ($object != "." && $object != "..") { if (is_dir($directory . DIR_SEP . $object)) { delete_directory($directory . DIR_SEP . $object); } else { unlink($directory . DIR_SEP . $object); } } } rmdir($directory, $context); } elseif (is_file($directory)) { unlink($directory, $context); } }多维数组转对象 /** * 多维数组转对象 * @param array $d * @return object */ function array_to_object(array $d): object { if (is_array($d)) { $json = json_encode($d); return json_decode($json); } else { return $d; } }多维对象转数组 /** * 多维对象转数组 * @param object|array $d * @return array */ function object_to_array($d) { if (is_object($d)) { $d = get_object_vars($d); // 获取给定对象的属性 } if (is_array($d)) { $function = __FUNCTION__; return array_map("self::$function", $d); } else { return $d; } }发送电子邮件 /** * 发送电子邮件 * @access public * @param string $title 邮件标题 * @param string $subtitle 邮件副标题 * @param string $content 邮件内容 * @param string $email 不填写则默认发送系统配置中的邮箱,优先程度:配置收件人>发信邮箱 * @return bool|object */ function send_mail($title, $subtitle, $content, $email = null) { $site = options('site'); $mail = options('mail'); if (empty($email)) { $email = empty($mail['recv']) ? $mail['account'] : $mail['recv']; } $PHPMailer = new \PHPMailer\PHPMailer\PHPMailer(); $language = $PHPMailer->setLanguage('zh_cn'); if (!$language) { return 'PHPMailer语言文件zh_cn加载失败'; } $PHPMailer->isSMTP(); $PHPMailer->SMTPAuth = true; $PHPMailer->CharSet = 'UTF-8'; $PHPMailer->SMTPSecure = $mail['secure']; // 允许 TLS 或者ssl协议 $PHPMailer->Host = $mail['smtp']; // SMTP服务器 $PHPMailer->Port = $mail['port']; // 服务器端口 25 或者465 $PHPMailer->FromName = $site['title']; //发件人昵称 $PHPMailer->Username = $mail['account']; //邮箱账号 $PHPMailer->From = $mail['account']; //邮箱账号 $PHPMailer->Password = $mail['pwd']; //邮箱密码 $PHPMailer->isHTML(true); $html = '<!DOCTYPE html><html lang="zh-cn"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"></head><body><style>.container{width:95%;margin:0 auto;border-radius:8px;font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;box-shadow:0 2px 12px 0 rgba(0,0,0,0.1);word-break:break-all}.title{color:#fff;background:linear-gradient(-45deg,rgba(9,69,138,0.2),rgba(68,155,255,0.7),rgba(117,113,251,0.7),rgba(68,155,255,0.7),rgba(9,69,138,0.2));background-size:400%400%;background-position:50%100%;padding:15px;font-size:15px;line-height:1.5}</style><div class="container"><div class="title">{title}</div><div style="background: #fff;padding: 20px;font-size: 13px;color: #666;"><div style="margin-bottom: 20px;line-height: 1.5;">{subtitle}</div><div style="padding: 15px;margin-bottom: 20px;line-height: 1.5;background: repeating-linear-gradient(145deg, #f2f6fc, #f2f6fc 15px, #fff 0, #fff 25px);">{content}</div><div style="line-height: 2">请注意:此邮件由系统自动发送,请勿直接回复。<br>若此邮件不是您请求的,请忽略并删除!</div></div></div></body></html>'; $PHPMailer->Body = strtr( $html, [ "{title}" => $title . ' - ' . $site['title'], "{subtitle}" => $subtitle, "{content}" => $content, ] ); $PHPMailer->addAddress($email); $PHPMailer->Subject = $title . ' - ' . $site['title']; if ($PHPMailer->send()) { return true; } else { return $PHPMailer->ErrorInfo; } }获取配置信息 function options($name = false, $value = false) { if ($name === false && $value == false) { return Options::all(); } if (func_num_args() == 1) { returnOptions::get($name); } return Options::save($name, $value); } class Options { private static $options = null; public static function initialize() { if (is_null(self::$options)) { $select = Db::name('options')->select(); self::$options = []; foreach ($select as $value) { self::$options[$value['name']] = $value['value']; } } } public static function get($name) { $name_list = explode('.', $name); // 将路径分解成键名数组 $value = self::$options; // 初始化$value为数组的根 // 遍历键名数组,逐级深入到数组中 foreach ($name_list as $name_value) { if (isset($value[$name_value])) { $value = $value[$name_value]; // 如果键存在,更新$value } else { return null; // 如果键不存在,返回null或者你可以选择抛出异常 } } return $value; // 返回最终找到的值 } public static function all() { return self::$options; } public static function save(string $name, $value) { if (array_key_exists($name, self::$options)) { $save = self::update($name, $value); } else { $save = self::insert($name, $value); } // 保存虚拟模型数据 self::model($name, $value); return $save; } public static function insert(string $name, $value) { // 插入虚拟模型数据 self::model($name, $value); // 插入数据库数据 return Db::name('options')->insert(['name' => $name, 'value' => $value]); } /** * 设置虚拟模型数据 */ public static function model(string $name, $value) { self::$options[$name] = $value; return true; } public static function update(string $name, $value) { // 更新虚拟模型数据 self::model($name, $value); // 更新数据库数据 return Db::name('options')->where('name', $name)->update(['value' => $value]); } }以数组的形式返回关于文件路径的信息 pathinfo(path, options); /** 返回的数组元素如下: ['dirname']: 目录路径 ['basename']: 文件名 ['extension']: 文件后缀名 ['filename']: 不包含后缀的文件名 **/ // path:规定要检查的路径。 /** options: PATHINFO_DIRNAME - 只返回 dirname PATHINFO_BASENAME - 只返回 basename PATHINFO_EXTENSION - 只返回 extension PATHINFO_FILENAME - 只返回 filename **/获取PHP代码执行后的结果 <?php function run_code($code) { $file_name = rand() . 'txt'; $content = '<?php $text = ' . $code . ' ?>'; file_put_contents($file_name, $content); include($file_name); unlink($file_name); return $text; } ?>多维数组降维 function ArrMd2Ud($arr) { #将数值第一元素作为容器,作地址赋值。 $ar_room = &$arr[key($arr)]; #第一容器不是数组进去转呀 if (!is_array($ar_room)) { #转为成数组 $ar_room = array($ar_room); } #指针下移 next($arr); #遍历 while (list($k, $v) = each($arr)) { #是数组就递归深挖,不是就转成数组 $v = is_array($v) ? call_user_func(__FUNCTION__, $v) : array($v); #递归合并 $ar_room = array_merge_recursive($ar_room, $v); #释放当前下标的数组元素 unset($arr[$k]); } return $ar_room; }字符串加密解密 /** * $string:需要加解密的字符串; * $operation:E表示加密,D表示解密; * $key:自定义密匙 */ function string($string, $operation, $key = '') { $key = md5($key); $key_length = strlen($key); $string = $operation == 'D' ? base64_decode($string) : substr(md5($string . $key), 0, 8) . $string; $string_length = strlen($string); $rndkey = $box = array(); $result = ''; for ($i = 0; $i <= 255; $i++) { $rndkey[$i] = ord($key[$i % $key_length]); $box[$i] = $i; } for ($j = $i = 0; $i < 256; $i++) { $j = ($j + $box[$i] + $rndkey[$i]) % 256; $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp; } for ($a = $j = $i = 0; $i < $string_length; $i++) { $a = ($a + 1) % 256; $j = ($j + $box[$a]) % 256; $tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp; $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); } if ($operation == 'D') { if (substr($result, 0, 8) == substr(md5(substr($result, 8) . $key), 0, 8)) { return substr($result, 8); } else { return ''; } } else { return str_replace('=', '', base64_encode($result)); } }图片
-
如何利用个人博客赚钱 个人博客如何赚钱盈利图片 最近在研究个人博客赚钱,看了很多关于技术人员如何赚钱的文章,但都很肤浅,没有提供具体的方法和策略,也就是赚钱的“技巧”和“途径”。本文将与你分享基于个人博客的赚钱方法和途径。乔布斯和比尔·盖茨 1981年,在苹果公司总部,召开了个人电脑领域的21世纪会议。乔布斯与比尔·盖茨讨论了合作事宜,并向比尔·盖茨展示了新设计的麦金塔电脑。图形界面的操作震惊了比尔·盖茨,但也为乔布斯制造了一个强大的对手。 这个故事想说明两点:第一,分享这些干货会不会给自己培养对手,因为你也是做个人博客的;二、解释“艺”与“道”的区别。乔布斯更多用艺术来创新,比尔盖茨更多用道来整合利润。 本文阐述如何通过个人博客赚钱,也是基于“术”与“道”的融合。 不听也不问 你可能听说过,个人博客已经不赚钱了。那个时代已经过去了。你可能也在搜索“做个人博客还能赚钱吗”?对于这种问题,不要听也不要问,直接去做。 所谓“后那个时代”,只是过了那个暴利时代,但不代表不赚钱。通过搜索引擎,你可以找到数以千计的网站,这些网站每天产生数以亿计的新内容。 如果不赚钱,为什么会这样?不赚钱的可能只是其中的一部分方式。 思维的改变 如果你想通过写博客赚钱,请先丢弃那些千篇一律的“专家”建议,比如:如何保持激情,如何坚持写作,如何养成良好的写作习惯,如何创新内容。都是基于“艺术”的层面,都是一样正确的废话。 现在告诉你,如果你想通过博客变得正确,你首先要改变你的角色:你现在不是一个博主,而是一个创业公司的CEO。你需要像CEO一样经营你的公司,像CEO一样思考,而不仅仅是一种爱好。 让个人博客从爱好变成一个企业的产品,让个人博客从“术”的层面变成“道”的层面进行思考和规划。如果你说写博客纯粹是爱好,不想赚钱,那就没必要读下去了。 根据市场选择 根据市场需求选择博客内容,而不仅仅是热情。说起博客失败的原因,95%的人认为是因为不够热情。如果通过写博客可以每天获得大量收入,你会热情吗? 个人博客失败的原因和经营企业一样:无利可图。 现在看看你的个人博客,你是在玩你的爱好吗?今天的爱好,明天的兴趣,后天的感受... 如果一个企业管理一个产品,会这么随便吗?会不会包罗万象?这样,你只会变得精疲力尽,无利可图。 现在从博主变成企业经营者。 作为一个商业运营者,你应该考虑的不是带着激情写作,而是分析你所选择的细分市场的商业因素:受众预算潜力、专业影响力和市场需求。 受众预算潜力 一个产品成功的核心因素是什么?当然可以解决用户的痛点,给用户提供价值。同时考虑用户愿意为此付出多少。 首先,你是博客的受众。推而广之,想想你提供的内容能给受众带来什么。然后,结合自己的专业知识,提供专业的内容和解决方案。 如果你说,我只是想写篇文章发泄一下情绪。这个时候,虽然你天然是观众,但对大部分用户来说是没用的。 像技术圈比较知名的个人博客:廖雪峰的个人博客,阮一峰的个人博客。他们都是为入门级技术人员提供有价值的内容,然后通过广告和销售在线课程来盈利。 广告是“羊毛出在猪身上”的模式,在线课程的定位也不错。新程序员愿意支付一定的学习费用,也有支付能力。 这些都是比较传统的做法,但是如果你作为一个公司来运营,你考虑做一些事情来增加几十倍甚至上百倍的收益吗?这个想法很简单。选择更丰富的受众,能够为他们提供核心服务。 举个简单的例子,如果你的专业技能能让你设计出很多好的方案。如果只是把这些方案发布在个人博客上,然后通过流量赚取广告费,收入可能少得可怜。但是如果你把这个包装成一个产品,从B2C走向B2B,为企业提供解决方案,你的收入会翻倍。 这个过程的时间成本和运营成本几乎相同,但收益却翻倍。 提炼:在选择细分市场的时候,优先考虑受众能给你带来多少收益,思考是否能通过ToC转化为ToB。靠近最有利可图的地区。 专业影响力 选好受众,给他们提供核心价值观之后,接下来就是发掘比别人更好的战略优势。 问问你自己: 我目前的创业能力如何?到什么程度? 我在这个行业有什么资源? 我能利用我的专业经验建立一个盈利的博客吗? 谁会从一开始就看重我做的事情? 要建立一个成功的博客,最重要的是建立影响力,树立权威。这些都需要你走出自己的舒适区,充分利用自己的人脉资源、专业知识以及一切相关的东西。 有人会问,写个人博客就行了。有必要这么受欢迎吗?别忘了,你现在是在做生意。举两个知识付费圈的例子:罗辑思维和樊登读书。 罗辑思维一开始只是在优酷上分享一个每周一次的知识视频脱口秀,然后在微信官方账号上每天更新一个语音,建立自己的个人影响力,然后就有了Get这个产品。 在这个过程中,罗胖动用了几乎所有的个人资源,专业知识,突破舒适区的学习能力,毅力等等。(打个广告,我是微信微信官方账号:节目新视界,一直在持续更新干货) 樊登读书会也是如此,采用线下和线下两种方式。这些对于个人博客(甚至其他内容平台)的运营都是可以借鉴的。 知乎V成功回答问题,获得粉丝,并将其引导到个人产品或平台的例子不胜枚举。至于个人博客,涉及到SEO中的反向链接,也是建立影响力的形式之一。 另一方面,如果你有很强的背景(来自BAT或ATM),也可以放在个人博客上作为信用背书。 提炼:打造品牌,建立影响力,利用一切可以利用的资源来经营你的“产品”,而不仅仅是凭激情,三天打鱼两天晒网。 市场需求 考虑了受众,建立了品牌之后,就该考察如何盈利了。搜索一下,看看有没有人在搜索你的话题,并为解决方案付费。 在你的小众市场选择5到10个关键词,然后使用关键词分析工具(Google关键词策划大使、百度指数)或者更好的SEO工具(比如Ahrefs)进行分析。 如果你的前5个关键词的搜索量达到一定程度(比如每个月至少5000次),说明有人对这样的话题感兴趣。 此时需要注意关键词的难度评分(从易到难0-100)。如果你的关键词难度分在80以上,可能竞争太激烈了,没什么影响。搜索一个月5000次以上,难度系数低于40的目标关键词,这样可以在竞争较少的情况下获得更多的流量。 另一种竞争方式是使用类似Ahrefs的SEO工具来查看搜索引擎结果页面(SERP)数据。具体来说,寻找域名当局(DA)和首页结果的SERP位置历史。 如果前10个结果的DA都超过65,阿达值为0的新博客就不会进入第一页,所以得不到访客。但是如果你在首页看到DA在30以下的网站,你可以通过他们提供更好的内容。如果SERP的持仓历史在过去6个月没有变化,那么竞争可能太激烈了。 回顾研究市场需求的几个要点: 每月超过5000次搜索。 关键词难度分值在0.4以下。 谷歌首页至少有一个网站的阿达低于30。 最近改变的SERP头寸的历史。 “专家”告诉你继续写,但你要把经验集中在受众预算潜力、专业影响力、市场需求等领域。 试想一下,如果你是一家公司的CEO,你应该更关注什么?是市场,是用户,是盈利模式,而不是每一行具体的代码。 归根结底:激情不创造金钱,金钱创造激情。如果你想赚钱,先停止“激情”创作。 运行并扩展您的博客 停止大量写作,开始扩展你的博客。写作必不可少,但“酒香也怕巷子深”。很多好的产品不能被更多的人使用。关键在于操作。 大多数个人博客更侧重于写作,而不是运营。为什么公司愿意高薪聘请优秀的销售人员?因为这个环节太重要了。而技术人员的个人博客更容易忽略运营和推广的重要性。 操作的主要组件: 建立和扩大关系 访客留言 外向 反向链接 无限重复上述步骤。 为什么会有商会和联盟?即众人拾柴火焰高。通过筛选高权重的博客,然后和他们建立关系,你就可以通过转发、留言、友情链接等方式,把你在高权重的网站上创作的内容传播出去。 关系的基础是什么?就是你能给别人提供价值,这就提供了筛选的标准。比如你想在一个网站上发表文章,保留这个网站的链接,那么你提供的文章首先必须是有价值的,至少可以弥补他们博客内容的不足。 内容匹配和受众匹配可以弥补自己博客内容的不足,是通过同行拓展自己博客的最好标准。 当这样的关系建立之后,剩下的事情就由搜索引擎替你处理了,可以快速获得流量和权重。 核心:链接建设是最难掌握的SEO策略,也是最有价值的。想办法扩展,让你的网站建立更多的链接。 如何正确盈利 有了内容,有了访客,有了流量,其实盈利是自然而然的。通过博客赚钱的方式有很多:营销联盟、广告、一对一辅导、咨询、实体销售、电子书销售、在线课程、知识付费等。 在挣钱的过程中,你还需要有一个时间表来进行不同的操作来对抗。 在新建立的网站上贴满广告是没有意义的,因为没有流量,而且会阻碍流量的发展。联盟营销也是如此。只有达到一定的用户基数,一次性销售才能达到效果。 能产生“被动收入”的网络营销、网络课程、广告收入、数码产品,可能需要几年时间,但毕竟是“睡后”收入。 如果你想在短时间内赚钱?那么可以考虑以下几种方式:高端咨询和会员制产品。然后就是在线课程、广告、会员产品等。 高端咨询主要靠你长期的专业知识积累,而会员产品也需要专业的输出。高端咨询可以为个人提供一对一辅导,为企业提供内训。会员制产品类似于Get、知识星球等服务类产品,购买一年。 在整个盈利过程中,请参考以下规则: 低流量x高价格=高流量x低价格流量低时提供高价服务产品,流量高时选择低价产品。 你的博客能赚多少钱? 如果通过激情创作来博客,可能根本没有收入。但是如果你像做生意一样经营你的博客,那么一个成功的博客每年可以赚到七位数。 现在,是时候开始你的博客之旅了。当然,如果你有更有效的方案,也可以分享给大家。
-
PHP使用PDO连接数据库 一、什么是PDO? PDO是PHP Date Object(PHP数据对象)的简称,它是与PHP 5.1版本一起发行的。 目前支持的数据库包括Firebird、FreeTDS、Interbase、MySQL、MS SQL Server、ODBC、Oracle、Postgre SQL、SQLite和Sybase。 PHP开发图片 有了PDO,您不必再使用mysql_函数、oci_函数或者mssql_*函数,也不必再为它们封装数据库操作类,只需要使用PDO接口中的方法就可以对数据库进行操作。在选择不同的数据库时,只需修改PDO的DSN(数据源名称)。 在PHP 6中将默认使用PDO连接数据库,所有非PDO扩展将会在PHP 6中被移除。该扩展提供PHP内置类PDO来对数据库进行访问,不同数据库使用相同的方法名,以解决数据库连接不统一的问题。 二、PDO的作用与特点 统一各种数据库的访问接口 PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mssql函数库相比,PDO让跨数据库的使用更具有亲和力;与ADODB和MDB2相比,PDO更高效。 图片 三、PDO的启用 PDO是与PHP 5.1一起发行的,默认包含在PHP 5.1中。 由于PDO需要PHP 5.0核心面向对象特性的支持,因此其无法在PHP 5.0之前的版本中使用。 默认情况下,PDO在PHP 5.2中为开启状态,但是要启用对某个数据库驱动程序的支持,仍需要进行相应的配置操作。 在Windows环境下,PDO在php.ini文件中进行配置,如果想支持某个特定数据库,只需要把php.ini 文件里边所对应的 ;号 去掉即可。 我们先找到php.ini 配置文件,然后进入 搜索 pdo_ 可以看到我们这里只打开了 mysql和sqlite。注意:开启过后需要重启Apache 图片 我们可以使用输出 phpinfo() 函数来查看是否开启。 四、通过PDO连接数据库 PDO的构造函数 在PDO中,要建立与数据库的连接需要实例化PDO的构造函数,PDO构造函数的语法如下: __construct(string $dsn[,string $username[,string $password[,array $driver_options]]]) dsn:数据源名,包括主机名、端口号和数据库名称。 username:连接数据库的用户名。 password:连接数据库的密码。 driver_options:连接数据库的其他选项。 格式:mysql,db_databvase17,127.0.0.1,root,123456 实例: $dbms='mysql';//数据库类型 $host='127.0.0.1';//数据库主机名 也可以是127.0.0.1 $dbName='db_database17';//使用的数据库名称 $user='root';//用户名 $pass='123456';//对应的密码 $dsn="$dbms:dbname=$dbName;host=$host"; $conn= new PDO($dsn,$user,$pass); //初始化一个PDO对象,就是创建了连接数据库的对象 $conn构造函数用于构造PDO对象,有了这个对象,我们我就可以使用里边的方法来访问数据库。 1. exec() 方法 exec() 方法返回执行后受影响的行数,: 通常用于 INSERT 、 DELETE 和 UPDATE 语句中。 实例: <?php try { $conn = new PDO($dsn, $user, $pass); //初始化一个PDO对象,就是创建了连接数据库的对象 $conn $query = "insert into tb_pdo_mysql(pdo_type,database_name,dates)values('" . $_POST['pdo'] . "','" . $_POST['databases'] . "','" . $_POST['dates'] . "')"; $result = $conn->exec($query); echo "数据添加成功,受影响的行数为:" . $result; } catch (PDOException $e) { die("Error!:" . $e->getMessage() . '<br/>'); } ?>2. query() 方法 用于返回执行查询后的结果集 query() 方法用于返回执行查询后的结果集: 实例: <?php try { $pdo = new PDO($dsn, $user, $pass); $sql = 'select * from tb_pdo_mysql'; $result = $pdo->query($sql); foreach ($result as $row) { echo "<tr align='center'>" . "<td>" . $row['id'] . "</td>"; echo "<td>" . $row['pdo_type'] . "</td>"; echo "<td>" . $row['database_name'] . "</td>"; echo "<td>" . $row['dates'] . "</td>" . "</tr>"; } } catch (PDOException $e) { die("Error!:" . $e->getMessage() . '<br/>'); } ?>
-
Typecho开发主题常用函数及调用方法 1、站点名称 <?php $this->options->title() ?>2、站点网址 <?php $this->options ->siteUrl(); ?>3、完整路径标题如分享几个 Typecho 中常用的调用函数 <?php $this->archiveTitle(' » ', < span class="string">'', ' | '); ?><?php $this ->options->title(); ?>4、站点说明 <?php $this->options->description() ?>5、模板文件夹地址 <?php $this->options->themeUrl(); ?>6、导入模板文件夹内的 php 文件 <?php $this->need('.php'); ?>7、文章或者页面的作者 <?php $this->author(); ?>8、作者头像 < ?php $this->author->gravatar('40') ?> //此处输出的完整的img标签,40是头像的宽和高9、该文作者全部文章列表链接 <?php $this->author->permalink (); ?>10、该文作者个人主页链接 <?php $this->author->url(); ?>11 、该文作者的邮箱地址 <?php $this->author->mail(); ?>12、上一篇与下一篇调用代码 <?php $this->thePrev(); ?> <?php $this->theNext(); ?>13、判断是否为首页,输出相关内容 <?php if ($this->is('index')): ?> //首页输出内容 <?php else: ?> //不是首页输出内容 <?php endif; ?>14、文章或页面,评论数目 <?php $this->commentsNum('No Comments', '1 Comment' , '%d Comments'); ?>15、截取部份文章(首页每篇文章显示摘要),350 是字数 <?php $this->excerpt(350, '...'); ?>16、调用自定义字段 <?php $this->fields->fieldName ?>17、RSS 地址 <?php $this->options->feedUrl(); ?>18、获取最新 post <?php $this->widget('Widget_Contents_Post_Recent', 'pageSize=8&type=category')->parse('<li><a href="{permalink}">{title}</a></li>'); ?>19、纯文字分类名称,不带链接 <?php $this->category(',', false); ?>20、获取当前文章所属分类(包含链接) <?php if ($this->is('post')): ?> <span><?php $this->category(' '); ?></span> <?php endif; ?>21、获取文章分类列表 <ul> <?php $this->widget('Widget_Metas_Category_List') ->parse('<li><a href="{permalink}">{name}</a> ({count})</li>'); ?> </ul>22、获取某分类 post <ul> <?php $this->widget('Widget_Archive@indexyc', 'pageSize=8&type=category', 'mid=1') ->parse('<li><a href="{permalink}" title="{title}">{title}</a></li>'); ?> </ul>23、获取最新评论列表 <ul> <?php $this->widget('Widget_Comments_Recent')->to($comments); ?> <?php while($comments->next()): ?> <li><a href="<?php $comments->permalink(); ?>"><?php $comments->author(false); ?></a>: <?php $comments->excerpt(50, '...'); ?></li> <?php endwhile; ?> </ul>24、首页获取 最新文章 代码限制条数 <?php while ($this->next()): ?> <?php if ($this->sequence <= 3): ?> html <?php endif; ?> <?php endwhile; ?>25、获取最新评论列表第二个版本,只显示访客评论不显示博主也就是作者或者说自己发的评论 <?php $this->widget('Widget_Comments_Recent','ignoreAuthor=true')->to($comments); ?> <?php while($comments->next()): ?> <li><a href="<?php $comments->permalink(); ?>"><?php $comments->author(false); ?></a>: <?php $comments->excerpt(50, '...'); ?></li> <?php endwhile; ?>26、获取文章时间归档 <ul> <?php $this->widget('Widget_Contents_Post_Date', 'type=month&format=F Y') ->parse('<li><a href="{permalink}">{date}</a></li>'); ?> </ul>27、获取标签集合,也就是标签云 <?php $this->widget('Widget_Metas_Tag_Cloud', 'ignoreZeroCount=1&limit=28')->to($tags); ?> <?php while($tags->next()): ?> <a href="<?php $tags->permalink(); ?>" class="size-<?php $tags->split(5, 10, 20, 30); ?>"><?php $tags->name(); ?></a> <?php endwhile; ?>28、调用该文相关文章列表 <?php $this->related(5)->to($relatedPosts); ?> <?php if ($relatedPosts->have()): ?> //这句也可以写成 if (count($relatedPosts->stack)) <ul><?php while ($relatedPosts->next()): ?> <li><a href="<?php $relatedPosts->permalink(); ?>" title="<?php $relatedPosts->title(); ?>"><?php $relatedPosts->title(); ?></a></li> <?php endwhile; ?></ul> <?php else : ?> <li>无相关文章</li> <?php endif; ?>29、隐藏 head 区域的程序版本和模版名称 <?php $this->header("generator=&template="); ?>30、获取读者墙 <?php $period = time() - 999592000; // 時段: 30 天, 單位: 秒 $counts = Typecho_Db::get()->fetchAll(Typecho_Db::get() ->select('COUNT(author) AS cnt','author', 'url', 'mail') ->from('table.comments') ->where('created > ?', $period ) ->where('status = ?', 'approved') ->where('type = ?', 'comment') ->where('authorId = ?', '0') ->group('author') ->order('cnt', Typecho_Db::SORT_DESC) ->limit(25) ); $mostactive = ''; $avatar_path = 'http://www.gravatar.com/avatar/'; foreach ($counts as $count) { $avatar = $avatar_path . md5(strtolower($count['mail'])) . '.jpg'; $c_url = $count['url']; if ( !$c_url ) $c_url = Helper::options()->siteUrl; $mostactive .= "<a href='" . $c_url . "' title='" . $count['author'] . " (参与" . $count['cnt'] . "次互动)' target='_blank'><img src='" . $avatar . "' alt='" . $count['author'] . "的头像' class='avatar' width='32' height='32' /></a>\n"; } echo $mostactive; ?>31、登陆与未登录用户展示不同内容 <?php if($this->user->hasLogin()): ?> // 登陆可见 <?php else: ?> // 未登录和登陆均可见 <?php endif; ?>32、导航页面列表调用隐藏特定的页面 这个演示隐藏了 album 和 search 两个页面 <ul> <li<?php if($this->is('index')): ?> class="current"<?php endif; ?>><a href="<?php $this->options->siteUrl(); ?>">主页</a></li> <?php $this->widget('Widget_Contents_Page_List')->to($pages); ?> <?php while($pages->next()): ?> <?php if (($pages->slug != 'album') && ($pages->slug != 'search')): ?> <li<?php if($this->is('page', $pages->slug)): ?> class="current"<?php endif; ?>><a href="<?php $pages->permalink(); ?>" title="<?php $pages->title(); ?>"><?php $pages->title(); ?></a></li> <?php endif; ?> <?php endwhile; ?> </ul> //参数说明:9.0 版 typecho 支出在后台管理页面编辑时选择隐藏页面。33、Typecho 归档页面 (牧风提供) <?php $this->widget('Widget_Contents_Post_Recent', 'pageSize=10000')->to($archives); $year=0; $mon=0; $i=0; $j=0; $output = '<div id="archives">'; while($archives->next()): $year_tmp = date('Y',$archives->created); $mon_tmp = date('m',$archives->created); $y=$year; $m=$mon; if ($mon != $mon_tmp && $mon > 0) $output .= '</ul></li>'; if ($year != $year_tmp && $year > 0) $output .= '</ul>'; if ($year != $year_tmp) { $year = $year_tmp; $output .= '<h3 class="al_year">'. $year .' 年</h3><ul class="al_mon_list">'; //输出年份 } if ($mon != $mon_tmp) { $mon = $mon_tmp; $output .= '<li><span class="al_mon">'. $mon .' 月</span><ul class="al_post_list">'; //输出月份 } $output .= '<li>'.date('d日: ',$archives->created).'<a href="'.$archives->permalink .'">'. $archives->title .'</a> <em>('. $archives->commentsNum.')</em></li>'; //输出文章日期和标题 endwhile; $output .= '</ul></li></ul></div>'; echo $output; ?>34、获取当前文章页缩略图 <?php $this->attachments(1)->attachment->url(); ?> //进行熊掌号改造的朋友请务必注意,这里所谓缩略图指的是当前文章页第一个附件地址,请确保第一个附件类型为图片。35、根据页面类型显示内容 //判断是文章页则显示内容 <?php if ($this->is('post')): ?> 想要显示的内容1 <?php endif; ?> //判断是页面则显示内容 <?php if ($this->is('page', 'about')): ?> 想要显示的内容2 <?php endif; ?>图片
-
PHP操作文件详解 本文主要讲解php文件操作的方法和实例。希望对大家有帮助,操作方法仅供参考。 1、判断是否是一个文件 is_file('./test.txt'); //如果文件是常规的文件,该函数返回 true。 //该函数的结果会被缓存。请使用 clearstatcache() 来清除缓存。2、判断文件是否存在 file_exists('./test.txt');3、打开文件 $fp = fopen('./test.txt', 'r'); /** 只读, r 读写,文件覆盖:r+ 清空写入:w 可创建清空写入:w+ 追加写入:a 创建追加写入:a+ **/4、写入内容到文件 fwrite($fp, 'str字符串');5、指定读取多少字节的文件 fread($fp, 1024);6、一次性读取文件大小 filesize($path); fread($fp, filesize($path));7、关闭文件 fclose($fp);8、给文件重命名 rename(oldname, newname, context);9、删除文件 unlink($path);
-
PHP对API接口请求进行限流的几种算法 接口限流的意义 那么什么是限流呢?顾名思义,限流就是限制流量,包括一定时间内的并发流量和总流量。 就像你有一个 GB 流量的宽带包,用完就没了,所以控制好自己的使用频率和单次使用的总消费。 通过限流,我们可以很好地控制系统的 qps,从而达到保护系统或者接口服务器稳定的目的。 图片 接口限流的常用算法 1、计数器法 计数器法是限流算法里最简单也是最容易实现的一种算法。 比如我们规定,对于 A 接口来说,我们 1 分钟的访问次数不能超过 100 个。那么我们可以这么做:在一开始的时候,我们可以设置一个计数器 counter,每当一个请求过来的时候,counter 就加 1,如果 counter 的值大于 100 并且该请求与第一个请求的间隔时间还在 1 分钟之内,那么说明请求数过多; 如果该请求与第一个请求的间隔时间大于 1 分钟,且 counter 的值还在限流范围内,那么就重置 counter。 代码如下: class CounterDemo { private $first_request_time; private $request_count = 0; //已请求的次数 public $limit = 100; //时间窗口内的最大请求数 public $interval = 60; //时间窗口 s public function __construct() { $this->first_request_time = time(); } public function grant() { $now = time(); if ($now < $this->first_request_time + $this->interval) { //时间窗口内 if ($this->request_count < $this->limit) { $this->request_count++; return true; } else { return false; } } else { //超出前一个时间窗口后, 重置第一次请求时间和请求总次数 $this->first_request_time = $now; $this->request_count = 1; return true; } } } $m = new CounterDemo(); $n_success = 0; for ($i = 0; $i < 200; $i++) { $rt = $m->grant(); if ($rt) { $n_success++; } } echo '成功请求 ' . $n_success . ' 次';计数器算法很简单,但是有个严重的 bug: 一个恶意用户在 0:59 时瞬间发送了 100 个请求,然后再 1:00 时又瞬间发送了 100 个请求,那么这个用户在 2 秒内发送了 200 个请求。 上面我们规定 1 分钟最多处理 100 个请求, 也就是每秒 1.7 个请求。用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过系统的承载能力,导致系统挂起或宕机。 上面的问题,其实是因为我们统计的精度太低造成的。那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降低呢?我们可以看下面的滑动窗口算法。 图片 上图中,我们把一个时间窗口(一分钟)分成 6 份,每份(小格)代表 10 秒。每过 10 秒钟我们就把时间窗口往右滑动一格, 每一个格子都有自己独立的计数器。 比如一个请求在 0:35 秒到达的时候,就会落在 0:30-0:39 这个区间,并将此区间的计数器加 1。 从上图可以看出, 0:59 到达的 100 个请求会落在 0:50-0:59 这个灰色的格子中, 而 1:00 到达的 100 个请求会落在黄色的格子中。 而在 1:00 时间统计时, 窗口会往右移动一格,那么此时的时间窗口内的请求数量一共是 200 个,超出了限制的 100 个,触发了限流,后面的 100 个请求被抛弃或者等待。 如果我们把窗口时间划分越多, 比如 60 格,每格 1s, 那么限流统计会更精确。 2、漏桶算法 (Leaky Bucket) 漏桶算法(Leaky Bucket): 平滑网络上的突发流量。使其整流为一个稳定的流量。 图片 有一个固定容量的桶,有水流进来,也有水流出 去。对于流进来的水来说,我们无法预计一共有多少水会流进来,也无法预计水流的速度。但是对于流出去的水来说,这个桶可以固定水流出的速率。当桶满了之后,多余的水将会溢出(多余的请求会被丢弃)。 简单的算法实现代码: class LeakyBucketDemo { private $last_req_time; //上一次请求的时间 public $capacity; //桶的容量 public $rate; //水漏出的速度(个/秒) public $water; //当前水量(当前累积请求数) public function __construct() { $this->last_req_time = time(); $this->capacity = 100; $this->rate = 20; $this->water = 0; } public function grant() { $now = time(); $water = max(0, $this->water - ($now - $this->last_req_time) * $this->rate); // 先执行漏水,计算剩余水量 $this->water = $water; $this->last_req_time = $now; if ($water < $this->capacity) { // 尝试加水,并且水还未满 $this->water += 1; return true; } else { // 水满,拒绝加水 return false; } } } $m = new LeakyBucketDemo(); $n_success = 0; for ($i = 0; $i < 500; $i++) { $rt = $m->grant(); if ($rt) { $n_success++; } if ($i > 0 && $i % 100 == 0) { //每发起100次后暂停1s echo '已发送', $i, ', 成功 ', $n_success, ', sleep' . PHP_EOL; sleep(1); } } echo '成功请求 ' . $n_success . ' 次';3、令牌桶算法 (Token Bucket) 令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的(可用 token 数为 0),token 以一个固定的速率 r 往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。 实现代码如下: class TokenBucketDemo { private $last_req_time; //上次请求时间 public $capacity; //桶的容量 public $rate; //令牌放入的速度(个/秒) public $tokens; //当前可用令牌的数量 public function __construct() { $this->last_req_time = time(); $this->capacity = 100; $this->rate = 20; $this->tokens = 100; //开始给100个令牌 } public function grant() { $now = time(); $tokens = min($this->capacity, $this->tokens + ($now - $this->last_req_time) * $this->rate); // 计算桶里可用的令牌数 $this->tokens = $tokens; $this->last_req_time = $now; if ($this->tokens < 1) { // 若剩余不到1个令牌,则拒绝 return false; } else { // 还有令牌,领取1个令牌 $this->tokens -= 1; return true; } } } $m = new TokenBucketDemo(); $n_success = 0; for ($i = 0; $i < 500; $i++) { $rt = $m->grant(); if ($rt) { $n_success++; } if ($i > 0 && $i % 100 == 0) { //每发起100次后暂停1s echo '已发送', $i, ', 成功 ', $n_success, ', sleep' . PHP_EOL; sleep(1); } } echo '成功请求 ' . $n_success . ' 次';我们可以使用 redis 的队列作为令牌桶容器使用,使用 lPush(入队),rPop(出队),实现令牌加入与消耗的操作。 TokenBucket.php <?php /** * PHP基于Redis使用令牌桶算法实现接口限流,使用redis的队列作为令牌桶容器,入队(lPush)出队(rPop)作为令牌的加入与消耗操作。 * public add 加入令牌 * public get 获取令牌 * public reset 重设令牌桶 * private connect 创建redis连接 */ class TokenBucket { // class start private $_config; // redis设定 private $_redis; // redis对象 private $_queue; // 令牌桶 private $_max; // 最大令牌数 /** * 初始化 * @param Array $config redis连接设定 */ public function __construct($config, $queue, $max) { $this->_config = $config; $this->_queue = $queue; $this->_max = $max; $this->_redis = $this->connect(); } /** * 加入令牌 * @param Int $num 加入的令牌数量 * @return Int 加入的数量 */ public function add($num = 0) { // 当前剩余令牌数 $curnum = intval($this->_redis->lSize($this->_queue)); // 最大令牌数 $maxnum = intval($this->_max); // 计算最大可加入的令牌数量,不能超过最大令牌数 $num = $maxnum >= $curnum + $num ? $num : $maxnum - $curnum; // 加入令牌 if ($num > 0) { $token = array_fill(0, $num, 1); $this->_redis->lPush($this->_queue, ...$token); return $num; } return 0; } /** * 获取令牌 * @return Boolean */ public function get() { return $this->_redis->rPop($this->_queue) ? true : false; } /** * 重设令牌桶,填满令牌 */ public function reset() { $this->_redis->delete($this->_queue); $this->add($this->_max); } /** * 创建redis连接 * @return Link */ private function connect() { try { $redis = new Redis(); $redis->connect($this->_config['host'], $this->_config['port'], $this->_config['timeout'], $this->_config['reserved'], $this->_config['retry_interval']); if (empty($this->_config['auth'])) { $redis->auth($this->_config['auth']); } $redis->select($this->_config['index']); } catch (RedisException $e) { throw new Exception($e->getMessage()); return false; } return $redis; } } ?>令牌的假如与消耗: <?php /** * 演示令牌加入与消耗 */ require 'TokenBucket.php'; // redis连接设定 $config = array( 'host' => 'localhost', 'port' => 6379, 'index' => 0, 'auth' => '', 'timeout' => 1, 'reserved' => NULL, 'retry_interval' => 100, ); // 令牌桶容器 $queue = 'mycontainer'; // 最大令牌数 $max = 5; // 创建TrafficShaper对象 $tokenBucket = new TokenBucket($config, $queue, $max); // 重设令牌桶,填满令牌 $tokenBucket->reset(); // 循环获取令牌,令牌桶内只有5个令牌,因此最后3次获取失败 for ($i = 0; $i < 8; $i++) { var_dump($tokenBucket->get()); } // 加入10个令牌,最大令牌为5,因此只能加入5个 $add_num = $tokenBucket->add(10); var_dump($add_num); // 循环获取令牌,令牌桶内只有5个令牌,因此最后1次获取失败 for ($i = 0; $i < 6; $i++) { var_dump($tokenBucket->get()); } ?>