├── .gitignore ├── .DS_Store ├── demo.png ├── views ├── dark │ ├── page.php │ ├── post.php │ ├── 404.php │ ├── index.php │ ├── header.php │ └── layout.php └── light │ ├── page.php │ ├── 404.php │ ├── post.php │ ├── index.php │ ├── header.php │ └── layout.php ├── public ├── assets │ ├── logo.png │ └── markdown.png ├── markdown │ ├── github │ │ ├── 400.woff │ │ ├── 700.woff │ │ ├── 400i.woff │ │ ├── 600i.woff │ │ └── 700i.woff │ └── md.css ├── themes │ ├── dark.css │ └── light.css └── index.php ├── composer.json ├── pages ├── update.md ├── about.md └── install.md ├── posts ├── 2020-04-08-make_a_blog.md ├── 2020-04-13-add_author_and_face.md ├── 2020-05-02-theme_and_config.md └── 2020-04-10-hello.md ├── conf.yaml ├── README.md ├── composer.lock └── lib └── spyc.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/.DS_Store -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/demo.png -------------------------------------------------------------------------------- /views/dark/page.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /views/light/page.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/assets/logo.png -------------------------------------------------------------------------------- /public/assets/markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/assets/markdown.png -------------------------------------------------------------------------------- /public/markdown/github/400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/markdown/github/400.woff -------------------------------------------------------------------------------- /public/markdown/github/700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/markdown/github/700.woff -------------------------------------------------------------------------------- /public/markdown/github/400i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/markdown/github/400i.woff -------------------------------------------------------------------------------- /public/markdown/github/600i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/markdown/github/600i.woff -------------------------------------------------------------------------------- /public/markdown/github/700i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cr4fun/php-markdown-blog/HEAD/public/markdown/github/700i.woff -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "mikecao/flight": "^1.3", 4 | "erusev/parsedown": "^1.7" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /pages/update.md: -------------------------------------------------------------------------------- 1 | # 更新 2 | 3 | ### 2020-05-02 4 | 5 | 新增了footer底部的友情连接 6 | 7 | 去掉markdown风格。用户自定义风格,可以更改php模板结构。 8 | 9 | ### 2020-04-13 10 | 11 | 显示时间日期、作者及头像,主页博客按照倒叙显示。 12 | 13 | ### 2020-04-10 14 | 15 | PHP7.3的 markdown 博客系统 -------------------------------------------------------------------------------- /views/dark/post.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |

4 | 5 |
6 | -------------------------------------------------------------------------------- /views/dark/404.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 7 | 8 | 9 |

404

10 | 11 | -------------------------------------------------------------------------------- /views/light/404.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 7 | 8 | 9 |

404

10 | 11 | -------------------------------------------------------------------------------- /posts/2020-04-08-make_a_blog.md: -------------------------------------------------------------------------------- 1 | # 为何开发一个PHP的markdown博客系统 2 | 3 | 市面上已经有很多使用markdown的博客系统了。为什么我要开发一个PHP版本的? 4 | 5 | 实际上需求如下: 6 | 7 | 1、我不想在github pages上面部署博客。 8 | 9 | 2、我不想通过命令行生成静态页面。 10 | 11 | 3、python系统的云服务器成本太高。 12 | 13 | 因为,一个不需要输入命令,能够低成本运行的博客,就是我的需求。 14 | 15 | 于是,我做了这个博客系统。 -------------------------------------------------------------------------------- /views/light/post.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |

4 | 作者: 5 |    6 | 发布于: 7 |

8 | 9 |
10 | -------------------------------------------------------------------------------- /posts/2020-04-13-add_author_and_face.md: -------------------------------------------------------------------------------- 1 | # 增加了作者和头像 2 | 3 | 在conf.yaml中,增加了作者和头像。便于在首页及blog详情页面中显示。 4 | ``` 5 | author: cr4fun 6 | # 头像 更改为你自己的图片 7 | face: /assets/logo.png 8 | ``` 9 | 10 | 注意:2020-05-02 更新,conf.yaml中有关作者和头像的结构变更为: 11 | ``` 12 | author: 13 | name: cr4fun 14 | face: /assets/logo.png 15 | ``` -------------------------------------------------------------------------------- /views/dark/index.php: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | 7 | 8 | 9 | 10 |
11 | 14 |
-------------------------------------------------------------------------------- /views/light/index.php: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | 7 | 8 | 9 | 10 |
11 | 14 |
-------------------------------------------------------------------------------- /conf.yaml: -------------------------------------------------------------------------------- 1 | site: 2 | # 博客名称 3 | name: cr4fun的博客 4 | # 博客logo 5 | logo: /assets/logo.png 6 | 7 | # 主题 8 | theme: light 9 | 10 | # 作者 11 | author: 12 | name: cr4fun 13 | face: /assets/logo.png 14 | 15 | # 友情连接 16 | friendlinks: 17 | -item 18 | name: wireframe 19 | url: https://wireframe.cc 20 | -item 21 | name: figma 22 | url: https://www.figma.com 23 | -item 24 | name: invisionapp 25 | url: https://www.invisionapp.com/ -------------------------------------------------------------------------------- /posts/2020-05-02-theme_and_config.md: -------------------------------------------------------------------------------- 1 | # 主题和配置文件的变化 2 | 3 | 在conf.yaml中,和以前不太一样了。 4 | ``` 5 | site: 6 | # 博客名称 7 | name: cr4fun的博客 8 | # 博客logo 9 | logo: /assets/logo.png 10 | 11 | # 主题 12 | theme: light 13 | 14 | # 作者 15 | author: 16 | name: cr4fun 17 | face: /assets/logo.png 18 | 19 | # 友情连接 20 | friendlinks: 21 | -item 22 | name: wireframe 23 | url: https://wireframe.cc 24 | -item 25 | name: figma 26 | url: https://www.figma.com 27 | -item 28 | name: invisionapp 29 | url: https://www.invisionapp.com/ 30 | ``` -------------------------------------------------------------------------------- /views/dark/header.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /views/light/header.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /pages/about.md: -------------------------------------------------------------------------------- 1 | # 关于 2 | 3 | 静态博客有很多种,我也不想重复造轮子。 4 | 5 | 以前用jekyll、hexo。后来用next.js生成react的静态博客。 6 | 7 | 但总是觉得,写博客的核心,其实就是markdown。至于是不是在线的,是不是需要有后台界面,其实不重要。 8 | 9 | 我平时喜欢用typora,在线的没有这种体验。所以,直接用typora,写完后ftp上去就好了。 10 | 11 | 这就是 php-markdown-blog 的由来。 12 | 13 | 它基于PHP7.2,无需数据库,直接使用markdown。 14 | 15 | 它采用小众的php框架:[flight](http://flightphp.com/) ,十分轻巧。 16 | 17 | 它面向的是不想把博客部署在github pages上的用户。 18 | 19 | 它不需要生成静态页面。它成本低,python主机很贵,但PHP主机很便宜。 20 | 21 | 因此,这个系统只要上传到PHP版本为7.2及以上的PHP虚拟主机上,就可以使用了。 22 | 23 | 它面向的是ftp主义者。只需要写好markdown,ftp到服务器上就可以了。 24 | 25 | 最后,欢迎 fork,欢迎 star! 26 | 27 | https://github.com/cr4fun/php-markdown-blog 28 | 29 | -------------------------------------------------------------------------------- /views/dark/layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <?php echo $site['name']?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | -------------------------------------------------------------------------------- /views/light/layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <?php echo $site['name']?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | -------------------------------------------------------------------------------- /pages/install.md: -------------------------------------------------------------------------------- 1 | # 安装及使用方法 2 | 3 | ## 安装 4 | 5 | ``` 6 | git clone https://github.com/cr4fun/php-markdown-blog 7 | cd php-markdown-blog 8 | comporser install 9 | ``` 10 | 11 | ## 配置 12 | 13 | conf.yaml 14 | 15 | ``` 16 | site: 17 | # 博客名称 18 | name: cr4fun的博客 19 | # 博客logo 20 | logo: /assets/logo.png 21 | 22 | # 主题 23 | theme: light 24 | 25 | # 作者 26 | author: 27 | name: cr4fun 28 | face: /assets/logo.png 29 | 30 | # 友情连接 31 | friendlinks: 32 | -item 33 | name: wireframe 34 | url: https://wireframe.cc 35 | -item 36 | name: figma 37 | url: https://www.figma.com 38 | -item 39 | name: invisionapp 40 | url: https://www.invisionapp.com/ 41 | ``` 42 | 43 | ## 运行 44 | 45 | ``` 46 | cd public 47 | php -S 0.0.0.0:3000 48 | ``` 49 | 50 | ## 如何添加博客文章 51 | 52 | markdown文档,放在posts文件夹里。 53 | 54 | 需要注意遵循格式: 55 | 56 | 年-月-日-文件名称.md 57 | 58 | 文件名称不可以包括 - 可以使用下划线,不能有空格。markdown的第一行必须有大标题。 59 | 60 | ## 如何添加页面 61 | 62 | markdown文档,放在pages文件夹里。 63 | 64 | 需要注意遵循格式: 65 | 66 | 文件名称.md 67 | 68 | 文件名称不可以包括符号,不能有空格。markdown的第一行必须有大标题。 69 | 70 | ## 图片应该放在那里? 71 | 72 | 放在 public\assets 文件夹里。 73 | 74 | ``` 75 | ![](/assets/logo.png) 76 | ``` 77 | 78 | ![](/assets/logo.png) -------------------------------------------------------------------------------- /public/themes/dark.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin:0; 3 | padding:0; 4 | } 5 | body{ 6 | background: #000; 7 | } 8 | .main{ 9 | margin:0 auto; 10 | width: 94%; 11 | max-width: 1000px; 12 | } 13 | .navbar{ 14 | background: #fff; 15 | box-shadow: 0px 0px 5px 0px #ccc; 16 | } 17 | .flex{ 18 | display: flex; 19 | justify-content: space-between; 20 | align-items: center; 21 | } 22 | .logo{ 23 | width: 30px; 24 | height: 30px; 25 | } 26 | .site_name{ 27 | margin-left:10px; 28 | color:#555; 29 | } 30 | .navs{ 31 | height: 50px; 32 | line-height: 50px; 33 | } 34 | .navs .nav{ 35 | float: right; 36 | margin-left: 10px; 37 | font-weight: 900; 38 | } 39 | a{ 40 | color:#555; 41 | text-decoration: none; 42 | } 43 | a:hover{ 44 | color:#555; 45 | } 46 | .post_item{ 47 | margin-top:20px; 48 | display: flex; 49 | background: #fff; 50 | padding: 10px; 51 | border-radius: 5px; 52 | } 53 | .full{ 54 | width:100%; 55 | } 56 | .markdown{ 57 | color:#fff; 58 | } 59 | code{ 60 | display: block; 61 | padding: 10px; 62 | border-radius: 6px; 63 | background: #fff; 64 | color:#000; 65 | } 66 | footer{ 67 | text-align: center; 68 | background: rgb(34, 34, 34); 69 | padding-top: 20px; 70 | padding-bottom:20px; 71 | margin-top:20px; 72 | color:#999; 73 | } 74 | footer p { 75 | margin-bottom: 0; 76 | padding:10px; 77 | } 78 | footer a{ 79 | color:#999; 80 | } -------------------------------------------------------------------------------- /public/themes/light.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin:0; 3 | padding:0; 4 | } 5 | body{ 6 | background: #f6f6f6; 7 | } 8 | .main{ 9 | margin:0 auto; 10 | width: 94%; 11 | max-width: 1000px; 12 | } 13 | .navbar{ 14 | background: #fff; 15 | box-shadow: 0px 0px 5px 0px #ccc; 16 | } 17 | .flex{ 18 | display: flex; 19 | justify-content: space-between; 20 | align-items: center; 21 | } 22 | .flex_start{ 23 | justify-content: flex-start; 24 | } 25 | .logo{ 26 | width: 30px; 27 | height: 30px; 28 | } 29 | .site_name{ 30 | margin-left:10px; 31 | color:#555; 32 | } 33 | .navs{ 34 | height: 50px; 35 | line-height: 50px; 36 | } 37 | .navs .nav{ 38 | float: right; 39 | margin-left: 10px; 40 | font-weight: 900; 41 | } 42 | a{ 43 | color:#555; 44 | text-decoration: none; 45 | } 46 | a:hover{ 47 | color:#555; 48 | } 49 | .post_item{ 50 | margin-top:20px; 51 | display: flex; 52 | background: #fff; 53 | padding: 10px; 54 | border-radius: 5px; 55 | } 56 | .face{ 57 | margin-right:10px; 58 | } 59 | .full{ 60 | width:100%; 61 | } 62 | code{ 63 | display: block; 64 | padding: 10px; 65 | border-radius: 6px; 66 | } 67 | footer{ 68 | text-align: center; 69 | background: rgb(34, 34, 34); 70 | padding-top: 20px; 71 | padding-bottom:20px; 72 | margin-top:20px; 73 | color:#999; 74 | } 75 | footer p { 76 | margin-bottom: 0; 77 | padding:10px; 78 | } 79 | footer a{ 80 | color:#999; 81 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-markdown-blog 2 | 3 | ## 介绍 4 | 5 | 静态博客有很多,我也不想重复造轮子。所以开发一个特别的。它基于PHP7.2,无需数据库,直接使用markdown。 6 | 7 | 它面向的是,不想把博客部署在github pages上的用户。它不需要生成静态页面。它成本低,python主机很贵,但PHP主机很便宜。 8 | 9 | 因此,这个系统只要上传到PHP版本为7.2及以上的PHP虚拟主机上,就可以使用了。 10 | 11 | ## 如何添加博客文章 12 | 13 | markdown文档,放在posts文件夹里。 14 | 15 | 需要注意遵循格式: 16 | 17 | 年-月-日-文件名称.md 18 | 19 | 文件名称不可以包括 - 可以使用下划线,不能有空格。markdown的第一行必须有大标题。 20 | 21 | ## 如何添加页面 22 | 23 | markdown文档,放在pages文件夹里。 24 | 25 | 需要注意遵循格式: 26 | 27 | 文件名称.md 28 | 29 | 文件名称不可以包括符号,不能有空格。markdown的第一行必须有大标题。 30 | 31 | ## 图片应该放在那里? 32 | 33 | 放在 public\assets 文件夹里。 34 | 35 | ``` 36 | ![](/assets/logo.png) 37 | ``` 38 | 39 | ![](/assets/logo.png) 40 | 41 | ## 安装 42 | 43 | ``` 44 | git clone https://github.com/cr4fun/php-markdown-blog 45 | cd php-markdown-blog 46 | comporser install 47 | ``` 48 | 49 | ## 配置 50 | 51 | conf.yaml 52 | 53 | ``` 54 | site: 55 | # 博客名称 56 | name: cr4fun的博客 57 | # 博客logo 58 | logo: /assets/logo.png 59 | 60 | # 主题 61 | theme: light 62 | 63 | # 作者 64 | author: 65 | name: cr4fun 66 | face: /assets/logo.png 67 | 68 | # 友情连接 69 | friendlinks: 70 | -item 71 | name: wireframe 72 | url: https://wireframe.cc 73 | -item 74 | name: figma 75 | url: https://www.figma.com 76 | -item 77 | name: invisionapp 78 | url: https://www.invisionapp.com/ 79 | ``` 80 | 81 | ## 运行 82 | 83 | ``` 84 | cd public 85 | php -S 0.0.0.0:3000 86 | ``` 87 | 88 | ## todo 89 | 90 | * 分页 91 | 92 | * 其他 93 | 94 | 95 | ## 欢迎 issue 96 | 97 | 欢迎大家写主题,主题在public/themes文件夹中。可在 conf.yaml 中设置。 98 | 99 | ## demo 100 | ![](demo.png) -------------------------------------------------------------------------------- /posts/2020-04-10-hello.md: -------------------------------------------------------------------------------- 1 | # 这是一篇示例文章 2 | > haha 3 | Headers 4 | --------------------------- 5 | 6 | # Header 1 7 | 8 | ## Header 2 9 | 10 | ### Header 3 11 | 12 | Styling 13 | --------------------------- 14 | 15 | *Emphasize* _emphasize_ 16 | 17 | **Strong** __strong__ 18 | 19 | ==Marked text.== 20 | 21 | ~~Mistaken text.~~ 22 | 23 | > Quoted text. 24 | 25 | H~2~O is a liquid. 26 | 27 | 2^10^ is 1024. 28 | 29 | 30 | Lists 31 | --------------------------- 32 | 33 | - Item 34 | * Item 35 | + Item 36 | 37 | 1. Item 1 38 | 2. Item 2 39 | 3. Item 3 40 | 41 | - [ ] Incomplete item 42 | - [x] Complete item 43 | 44 | 45 | Links 46 | --------------------------- 47 | 48 | A [link](http://example.com). 49 | 50 | An image: ![Alt](/assets/logo.png) 51 | 52 | 53 | Code 54 | --------------------------- 55 | 56 | Some `inline code`. 57 | 58 | ``` 59 | // A code block 60 | var foo = 'bar'; 61 | ``` 62 | 63 | ```javascript 64 | // An highlighted block 65 | var foo = 'bar'; 66 | ``` 67 | 68 | Tables 69 | --------------------------- 70 | 71 | Item | Value 72 | -------- | ----- 73 | Computer | $1600 74 | Phone | $12 75 | Pipe | $1 76 | 77 | 78 | | Column 1 | Column 2 | 79 | |:--------:| -------------:| 80 | | centered | right-aligned | 81 | 82 | 83 | Definition lists 84 | --------------------------- 85 | 86 | Markdown 87 | : Text-to-HTML conversion tool 88 | 89 | Authors 90 | : John 91 | : Luke 92 | 93 | Footnotes 94 | --------------------------- 95 | 96 | Some text with a footnote.[^1] 97 | 98 | [^1]: The footnote. 99 | 100 | 101 | Abbreviations 102 | --------------------------- 103 | 104 | Markdown converts text to HTML. 105 | 106 | *[HTML]: HyperText Markup Language -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "c7fbf2da3661e2ce40ee668f48f638fa", 8 | "packages": [ 9 | { 10 | "name": "erusev/parsedown", 11 | "version": "1.7.4", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/erusev/parsedown.git", 15 | "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", 20 | "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", 21 | "shasum": "", 22 | "mirrors": [ 23 | { 24 | "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", 25 | "preferred": true 26 | } 27 | ] 28 | }, 29 | "require": { 30 | "ext-mbstring": "*", 31 | "php": ">=5.3.0" 32 | }, 33 | "require-dev": { 34 | "phpunit/phpunit": "^4.8.35" 35 | }, 36 | "type": "library", 37 | "autoload": { 38 | "psr-0": { 39 | "Parsedown": "" 40 | } 41 | }, 42 | "notification-url": "https://packagist.org/downloads/", 43 | "license": [ 44 | "MIT" 45 | ], 46 | "authors": [ 47 | { 48 | "name": "Emanuil Rusev", 49 | "email": "hello@erusev.com", 50 | "homepage": "http://erusev.com" 51 | } 52 | ], 53 | "description": "Parser for Markdown.", 54 | "homepage": "http://parsedown.org", 55 | "keywords": [ 56 | "markdown", 57 | "parser" 58 | ], 59 | "time": "2019-12-30T22:54:17+00:00" 60 | }, 61 | { 62 | "name": "mikecao/flight", 63 | "version": "v1.3.7", 64 | "source": { 65 | "type": "git", 66 | "url": "https://github.com/mikecao/flight.git", 67 | "reference": "7d5970f7617861c868a5c728764421d8950faaf0" 68 | }, 69 | "dist": { 70 | "type": "zip", 71 | "url": "https://api.github.com/repos/mikecao/flight/zipball/7d5970f7617861c868a5c728764421d8950faaf0", 72 | "reference": "7d5970f7617861c868a5c728764421d8950faaf0", 73 | "shasum": "", 74 | "mirrors": [ 75 | { 76 | "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", 77 | "preferred": true 78 | } 79 | ] 80 | }, 81 | "require": { 82 | "php": ">=5.3.0" 83 | }, 84 | "require-dev": { 85 | "phpunit/phpunit": "~4.6" 86 | }, 87 | "type": "library", 88 | "autoload": { 89 | "files": [ 90 | "flight/autoload.php", 91 | "flight/Flight.php" 92 | ] 93 | }, 94 | "notification-url": "https://packagist.org/downloads/", 95 | "license": [ 96 | "MIT" 97 | ], 98 | "authors": [ 99 | { 100 | "name": "Mike Cao", 101 | "email": "mike@mikecao.com", 102 | "homepage": "http://www.mikecao.com/", 103 | "role": "Original Developer" 104 | } 105 | ], 106 | "description": "Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications.", 107 | "homepage": "http://flightphp.com", 108 | "time": "2018-11-23T23:05:33+00:00" 109 | } 110 | ], 111 | "packages-dev": [], 112 | "aliases": [], 113 | "minimum-stability": "stable", 114 | "stability-flags": [], 115 | "prefer-stable": false, 116 | "prefer-lowest": false, 117 | "platform": [], 118 | "platform-dev": [], 119 | "plugin-api-version": "1.1.0" 120 | } 121 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | $title,'link'=>$link); 44 | } 45 | return $links; 46 | } 47 | 48 | //从markdown文件里获得标题 49 | function gettitle($file){ 50 | $f= fopen($file,"r"); 51 | $title = fgets($f); 52 | fclose($f); 53 | $title = str_replace("# ","",$title); 54 | return $title; 55 | } 56 | 57 | function getpages(){ 58 | $pages = getlinks('../pages'); 59 | $pages[] = array('title'=>'首页','link'=> '/'); 60 | return $pages; 61 | } 62 | function getposts(){ 63 | $posts_new = array(); 64 | $posts = getlinks('../posts'); 65 | foreach($posts as $post){ 66 | $title = $post['title']; 67 | $link = $post['link']; 68 | $line_2_arr_str = str_replace("/","",$link); 69 | $arr = explode("-", $line_2_arr_str); 70 | $posttime = "$arr[0]-$arr[1]-$arr[2]"; 71 | $link = str_replace("-","/",$link); 72 | $posts_new[] = array('title'=>$title,'link'=>$link,'posttime'=>$posttime); 73 | } 74 | 75 | return array_reverse($posts_new); 76 | } 77 | 78 | 79 | 80 | $app->route('GET /', function(){ 81 | $pages = getpages(); 82 | $posts = getposts(); 83 | 84 | $conf = getconfig(); 85 | Flight::render('header', array( 86 | 'pages' => $pages, 87 | 'site' => $conf['site'] 88 | ), 'header_content'); 89 | Flight::render('index', array( 90 | 'site'=> $conf['site'], 91 | 'author' => $conf["author"], 92 | 'pages'=>$pages, 93 | 'posts'=>$posts 94 | ), 'body_content'); 95 | Flight::render('layout', array( 96 | 'site' => $conf['site'], 97 | 'theme_css' => $conf["theme"], 98 | 'links'=> $conf["friendlinks"] 99 | )); 100 | }); 101 | 102 | $app->route('GET /@n', function($n){ 103 | $conf = getconfig(); 104 | $pages = getpages(); 105 | $markdownfile = "../pages/$n.md"; 106 | if(file_exists($markdownfile)){ 107 | $Parsedown = new Parsedown(); 108 | $markdown_code = file_get_contents($markdownfile); 109 | $html_code = $Parsedown->text($markdown_code); 110 | $title = gettitle($markdownfile); 111 | 112 | Flight::render('header', array( 113 | 'pages' => $pages, 114 | 'site' => $conf['site'] 115 | ), 'header_content'); 116 | Flight::render('page', array( 117 | 'html' => $html_code, 118 | ), 'body_content'); 119 | Flight::render('layout', array( 120 | 'site' => $conf['site'], 121 | 'theme_css' => $conf["theme"], 122 | 'links'=> $conf["friendlinks"] 123 | )); 124 | }else{ 125 | Flight::render('404'); 126 | } 127 | }); 128 | 129 | $app->route('GET /@y/@m/@d/@n', function($y,$m,$d,$n){ 130 | $conf = getconfig(); 131 | $pages = getpages(); 132 | $markdownfile = "../posts/$y-$m-$d-$n.md"; 133 | if(file_exists($markdownfile)){ 134 | $Parsedown = new Parsedown(); 135 | $markdown_code = file_get_contents($markdownfile); 136 | $title = gettitle($markdownfile); 137 | $markdown_code = str_replace("# $title","",$markdown_code); 138 | $html_code = $Parsedown->text($markdown_code); 139 | 140 | Flight::render('header', array( 141 | 'pages' => $pages, 142 | 'site' => $conf['site'] 143 | ), 'header_content'); 144 | Flight::render('post', array( 145 | 'title' => $title, 146 | 'posttime' => "$y-$m-$d", 147 | 'author' => $conf['author'], 148 | 'html' => $html_code, 149 | ), 'body_content'); 150 | Flight::render('layout', array( 151 | 'site' => $conf['site'], 152 | 'theme_css' => $conf["theme"], 153 | 'links'=> $conf["friendlinks"] 154 | )); 155 | 156 | }else{ 157 | Flight::render('404'); 158 | } 159 | }); 160 | 161 | $app->map('notFound', function(){ 162 | Flight::render('404'); 163 | }); 164 | 165 | $app->start(); 166 | ?> 167 | -------------------------------------------------------------------------------- /public/markdown/md.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --side-bar-bg-color: #fafafa; 3 | --control-text-color: #777; 4 | } 5 | 6 | @include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext); 7 | 8 | @font-face { 9 | font-family: 'Open Sans'; 10 | font-style: normal; 11 | font-weight: normal; 12 | src: local('Open Sans Regular'),url('./github/400.woff') format('woff'); 13 | } 14 | 15 | @font-face { 16 | font-family: 'Open Sans'; 17 | font-style: italic; 18 | font-weight: normal; 19 | src: local('Open Sans Italic'),url('./github/400i.woff') format('woff'); 20 | } 21 | 22 | @font-face { 23 | font-family: 'Open Sans'; 24 | font-style: normal; 25 | font-weight: bold; 26 | src: local('Open Sans Bold'),url('./github/700.woff') format('woff'); 27 | } 28 | 29 | @font-face { 30 | font-family: 'Open Sans'; 31 | font-style: italic; 32 | font-weight: bold; 33 | src: local('Open Sans Bold Italic'),url('./github/700i.woff') format('woff'); 34 | } 35 | 36 | .markdown { 37 | font-size: 16px; 38 | } 39 | 40 | .markdown { 41 | font-family: "Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-serif; 42 | color: rgb(51, 51, 51); 43 | line-height: 1.6; 44 | } 45 | 46 | #write { 47 | max-width: 860px; 48 | margin: 0 auto; 49 | padding: 30px; 50 | padding-bottom: 100px; 51 | } 52 | #write > ul:first-child, 53 | #write > ol:first-child{ 54 | margin-top: 30px; 55 | } 56 | 57 | .markdown a { 58 | color: #4183C4; 59 | } 60 | 61 | .markdown h1, 62 | .markdown h2, 63 | .markdown h3, 64 | .markdown h4, 65 | .markdown h5, 66 | .markdownh6 { 67 | position: relative; 68 | margin-top: 1rem; 69 | margin-bottom: 1rem; 70 | font-weight: bold; 71 | line-height: 1.4; 72 | cursor: text; 73 | } 74 | .markdown h1:hover a.anchor, 75 | .markdown h2:hover a.anchor, 76 | .markdown h3:hover a.anchor, 77 | .markdown h4:hover a.anchor, 78 | .markdown h5:hover a.anchor, 79 | .markdown h6:hover a.anchor { 80 | text-decoration: none; 81 | } 82 | .markdown h1 tt, 83 | .markdown h1 code { 84 | font-size: inherit; 85 | } 86 | .markdown h2 tt, 87 | .markdown h2 code { 88 | font-size: inherit; 89 | } 90 | .markdown h3 tt, 91 | .markdown h3 code { 92 | font-size: inherit; 93 | } 94 | .markdown h4 tt, 95 | .markdown h4 code { 96 | font-size: inherit; 97 | } 98 | .markdown h5 tt, 99 | .markdown h5 code { 100 | font-size: inherit; 101 | } 102 | .markdown h6 tt, 103 | .markdown h6 code { 104 | font-size: inherit; 105 | } 106 | .markdown h1 { 107 | padding-bottom: .3em; 108 | font-size: 2.25em; 109 | line-height: 1.2; 110 | 111 | } 112 | .markdown h2 { 113 | padding-bottom: .3em; 114 | font-size: 1.75em; 115 | line-height: 1.225; 116 | 117 | } 118 | .markdown h3 { 119 | font-size: 1.5em; 120 | line-height: 1.43; 121 | } 122 | .markdown h4 { 123 | font-size: 1.25em; 124 | } 125 | .markdown h5 { 126 | font-size: 1em; 127 | } 128 | .markdown h6 { 129 | font-size: 1em; 130 | color: #777; 131 | } 132 | .markdown p, 133 | .markdown blockquote, 134 | .markdown ul, 135 | .markdown ol, 136 | .markdown dl, 137 | .markdown table{ 138 | margin: 0.8em 0; 139 | } 140 | .markdown li>ol, 141 | .markdown li>ul { 142 | margin: 0 0; 143 | } 144 | .markdown hr { 145 | height: 2px; 146 | padding: 0; 147 | margin: 16px 0; 148 | background-color: #e7e7e7; 149 | border: 0 none; 150 | overflow: hidden; 151 | box-sizing: content-box; 152 | } 153 | 154 | .markdown li p.first { 155 | display: inline-block; 156 | } 157 | .markdown ul, 158 | .markdown ol { 159 | padding-left: 30px; 160 | } 161 | .markdown ul:first-child, 162 | .markdown ol:first-child { 163 | margin-top: 0; 164 | } 165 | .markdown ul:last-child, 166 | .markdown ol:last-child { 167 | margin-bottom: 0; 168 | } 169 | .markdown blockquote { 170 | border-left: 4px solid #dfe2e5; 171 | padding: 0 15px; 172 | color: #777777; 173 | } 174 | .markdown blockquote blockquote { 175 | padding-right: 0; 176 | } 177 | .markdown table { 178 | padding: 0; 179 | word-break: initial; 180 | } 181 | .markdown table tr { 182 | border-top: 1px solid #dfe2e5; 183 | margin: 0; 184 | padding: 0; 185 | } 186 | .markdown table tr:nth-child(2n), 187 | thead { 188 | background-color: #f8f8f8; 189 | } 190 | .markdown table tr th { 191 | font-weight: bold; 192 | border: 1px solid #dfe2e5; 193 | border-bottom: 0; 194 | margin: 0; 195 | padding: 6px 13px; 196 | } 197 | .markdown table tr td { 198 | border: 1px solid #dfe2e5; 199 | margin: 0; 200 | padding: 6px 13px; 201 | } 202 | .markdown table tr th:first-child, 203 | .markdown table tr td:first-child { 204 | margin-top: 0; 205 | } 206 | .markdown table tr th:last-child, 207 | .markdown table tr td:last-child { 208 | margin-bottom: 0; 209 | } 210 | 211 | .CodeMirror-lines { 212 | padding-left: 4px; 213 | } 214 | 215 | .code-tooltip { 216 | box-shadow: 0 1px 1px 0 rgba(0,28,36,.3); 217 | border-top: 1px solid #eef2f2; 218 | } 219 | 220 | .md-fences, 221 | code, 222 | tt { 223 | border: 1px solid #e7eaed; 224 | background-color: #f8f8f8; 225 | border-radius: 3px; 226 | padding: 0; 227 | padding: 2px 4px 0px 4px; 228 | font-size: 0.9em; 229 | } 230 | 231 | code { 232 | background:#222; 233 | color: #fff; 234 | padding: 0 2px 0 2px; 235 | } 236 | 237 | .md-fences { 238 | margin-bottom: 15px; 239 | margin-top: 15px; 240 | padding-top: 8px; 241 | padding-bottom: 6px; 242 | } 243 | 244 | 245 | .md-task-list-item > input { 246 | margin-left: -1.3em; 247 | } 248 | 249 | @media print { 250 | html { 251 | font-size: 13px; 252 | } 253 | table, 254 | pre { 255 | page-break-inside: avoid; 256 | } 257 | pre { 258 | word-wrap: break-word; 259 | } 260 | } 261 | 262 | .md-fences { 263 | background-color: #f8f8f8; 264 | } 265 | #write pre.md-meta-block { 266 | padding: 1rem; 267 | font-size: 85%; 268 | line-height: 1.45; 269 | background-color: #f7f7f7; 270 | border: 0; 271 | border-radius: 3px; 272 | color: #777777; 273 | margin-top: 0 !important; 274 | } 275 | 276 | .mathjax-block>.code-tooltip { 277 | bottom: .375rem; 278 | } 279 | 280 | .md-mathjax-midline { 281 | background: #fafafa; 282 | } 283 | 284 | #write>h3.md-focus:before{ 285 | left: -1.5625rem; 286 | top: .375rem; 287 | } 288 | #write>h4.md-focus:before{ 289 | left: -1.5625rem; 290 | top: .285714286rem; 291 | } 292 | #write>h5.md-focus:before{ 293 | left: -1.5625rem; 294 | top: .285714286rem; 295 | } 296 | #write>h6.md-focus:before{ 297 | left: -1.5625rem; 298 | top: .285714286rem; 299 | } 300 | .md-image>.md-meta { 301 | /*border: 1px solid #ddd;*/ 302 | border-radius: 3px; 303 | padding: 2px 0px 0px 4px; 304 | font-size: 0.9em; 305 | color: inherit; 306 | } 307 | 308 | .md-tag { 309 | color: #a7a7a7; 310 | opacity: 1; 311 | } 312 | 313 | .md-toc { 314 | margin-top:20px; 315 | padding-bottom:20px; 316 | } 317 | 318 | .sidebar-tabs { 319 | border-bottom: none; 320 | } 321 | 322 | #typora-quick-open { 323 | border: 1px solid #ddd; 324 | background-color: #f8f8f8; 325 | } 326 | 327 | #typora-quick-open-item { 328 | background-color: #FAFAFA; 329 | border-color: #FEFEFE #e5e5e5 #e5e5e5 #eee; 330 | border-style: solid; 331 | border-width: 1px; 332 | } 333 | 334 | /** focus mode */ 335 | .on-focus-mode blockquote { 336 | border-left-color: rgba(85, 85, 85, 0.12); 337 | } 338 | 339 | header, .context-menu, .megamenu-content, footer{ 340 | font-family: "Segoe UI", "Arial", sans-serif; 341 | } 342 | 343 | .file-node-content:hover .file-node-icon, 344 | .file-node-content:hover .file-node-open-state{ 345 | visibility: visible; 346 | } 347 | 348 | .mac-seamless-mode #typora-sidebar { 349 | background-color: #fafafa; 350 | background-color: var(--side-bar-bg-color); 351 | } 352 | 353 | .md-lang { 354 | color: #b4654d; 355 | } 356 | 357 | .html-for-mac .context-menu { 358 | --item-hover-bg-color: #E6F0FE; 359 | } 360 | 361 | #md-notification .btn { 362 | border: 0; 363 | } 364 | 365 | .dropdown-menu .divider { 366 | border-color: #e5e5e5; 367 | } 368 | 369 | .ty-preferences .window-content { 370 | background-color: #fafafa; 371 | } 372 | 373 | .ty-preferences .nav-group-item.active { 374 | color: white; 375 | background: #999; 376 | } -------------------------------------------------------------------------------- /lib/spyc.php: -------------------------------------------------------------------------------- 1 | 6 | * @author Chris Wanstrath 7 | * @link https://github.com/mustangostang/spyc/ 8 | * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen 9 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 10 | * @package Spyc 11 | */ 12 | 13 | if (!function_exists('spyc_load')) { 14 | /** 15 | * Parses YAML to array. 16 | * @param string $string YAML string. 17 | * @return array 18 | */ 19 | function spyc_load ($string) { 20 | return Spyc::YAMLLoadString($string); 21 | } 22 | } 23 | 24 | if (!function_exists('spyc_load_file')) { 25 | /** 26 | * Parses YAML to array. 27 | * @param string $file Path to YAML file. 28 | * @return array 29 | */ 30 | function spyc_load_file ($file) { 31 | return Spyc::YAMLLoad($file); 32 | } 33 | } 34 | 35 | if (!function_exists('spyc_dump')) { 36 | /** 37 | * Dumps array to YAML. 38 | * @param array $data Array. 39 | * @return string 40 | */ 41 | function spyc_dump ($data) { 42 | return Spyc::YAMLDump($data, false, false, true); 43 | } 44 | } 45 | 46 | if (!class_exists('Spyc')) { 47 | 48 | /** 49 | * The Simple PHP YAML Class. 50 | * 51 | * This class can be used to read a YAML file and convert its contents 52 | * into a PHP array. It currently supports a very limited subsection of 53 | * the YAML spec. 54 | * 55 | * Usage: 56 | * 57 | * $Spyc = new Spyc; 58 | * $array = $Spyc->load($file); 59 | * 60 | * or: 61 | * 62 | * $array = Spyc::YAMLLoad($file); 63 | * 64 | * or: 65 | * 66 | * $array = spyc_load_file($file); 67 | * 68 | * @package Spyc 69 | */ 70 | class Spyc { 71 | 72 | // SETTINGS 73 | 74 | const REMPTY = "\0\0\0\0\0"; 75 | 76 | /** 77 | * Setting this to true will force YAMLDump to enclose any string value in 78 | * quotes. False by default. 79 | * 80 | * @var bool 81 | */ 82 | public $setting_dump_force_quotes = false; 83 | 84 | /** 85 | * Setting this to true will forse YAMLLoad to use syck_load function when 86 | * possible. False by default. 87 | * @var bool 88 | */ 89 | public $setting_use_syck_is_possible = false; 90 | 91 | /** 92 | * Setting this to true will forse YAMLLoad to use syck_load function when 93 | * possible. False by default. 94 | * @var bool 95 | */ 96 | public $setting_empty_hash_as_object = false; 97 | 98 | 99 | /**#@+ 100 | * @access private 101 | * @var mixed 102 | */ 103 | private $_dumpIndent; 104 | private $_dumpWordWrap; 105 | private $_containsGroupAnchor = false; 106 | private $_containsGroupAlias = false; 107 | private $path; 108 | private $result; 109 | private $LiteralPlaceHolder = '___YAML_Literal_Block___'; 110 | private $SavedGroups = array(); 111 | private $indent; 112 | /** 113 | * Path modifier that should be applied after adding current element. 114 | * @var array 115 | */ 116 | private $delayedPath = array(); 117 | 118 | /**#@+ 119 | * @access public 120 | * @var mixed 121 | */ 122 | public $_nodeId; 123 | 124 | /** 125 | * Load a valid YAML string to Spyc. 126 | * @param string $input 127 | * @return array 128 | */ 129 | public function load ($input) { 130 | return $this->_loadString($input); 131 | } 132 | 133 | /** 134 | * Load a valid YAML file to Spyc. 135 | * @param string $file 136 | * @return array 137 | */ 138 | public function loadFile ($file) { 139 | return $this->_load($file); 140 | } 141 | 142 | /** 143 | * Load YAML into a PHP array statically 144 | * 145 | * The load method, when supplied with a YAML stream (string or file), 146 | * will do its best to convert YAML in a file into a PHP array. Pretty 147 | * simple. 148 | * Usage: 149 | * 150 | * $array = Spyc::YAMLLoad('lucky.yaml'); 151 | * print_r($array); 152 | * 153 | * @access public 154 | * @return array 155 | * @param string $input Path of YAML file or string containing YAML 156 | * @param array set options 157 | */ 158 | public static function YAMLLoad($input, $options = []) { 159 | $Spyc = new Spyc; 160 | foreach ($options as $key => $value) { 161 | if (property_exists($Spyc, $key)) { 162 | $Spyc->$key = $value; 163 | } 164 | } 165 | return $Spyc->_load($input); 166 | } 167 | 168 | /** 169 | * Load a string of YAML into a PHP array statically 170 | * 171 | * The load method, when supplied with a YAML string, will do its best 172 | * to convert YAML in a string into a PHP array. Pretty simple. 173 | * 174 | * Note: use this function if you don't want files from the file system 175 | * loaded and processed as YAML. This is of interest to people concerned 176 | * about security whose input is from a string. 177 | * 178 | * Usage: 179 | * 180 | * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); 181 | * print_r($array); 182 | * 183 | * @access public 184 | * @return array 185 | * @param string $input String containing YAML 186 | * @param array set options 187 | */ 188 | public static function YAMLLoadString($input, $options = []) { 189 | $Spyc = new Spyc; 190 | foreach ($options as $key => $value) { 191 | if (property_exists($Spyc, $key)) { 192 | $Spyc->$key = $value; 193 | } 194 | } 195 | return $Spyc->_loadString($input); 196 | } 197 | 198 | /** 199 | * Dump YAML from PHP array statically 200 | * 201 | * The dump method, when supplied with an array, will do its best 202 | * to convert the array into friendly YAML. Pretty simple. Feel free to 203 | * save the returned string as nothing.yaml and pass it around. 204 | * 205 | * Oh, and you can decide how big the indent is and what the wordwrap 206 | * for folding is. Pretty cool -- just pass in 'false' for either if 207 | * you want to use the default. 208 | * 209 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And 210 | * you can turn off wordwrap by passing in 0. 211 | * 212 | * @access public 213 | * @return string 214 | * @param array|\stdClass $array PHP array 215 | * @param int $indent Pass in false to use the default, which is 2 216 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) 217 | * @param bool $no_opening_dashes Do not start YAML file with "---\n" 218 | */ 219 | public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) { 220 | $spyc = new Spyc; 221 | return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes); 222 | } 223 | 224 | 225 | /** 226 | * Dump PHP array to YAML 227 | * 228 | * The dump method, when supplied with an array, will do its best 229 | * to convert the array into friendly YAML. Pretty simple. Feel free to 230 | * save the returned string as tasteful.yaml and pass it around. 231 | * 232 | * Oh, and you can decide how big the indent is and what the wordwrap 233 | * for folding is. Pretty cool -- just pass in 'false' for either if 234 | * you want to use the default. 235 | * 236 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And 237 | * you can turn off wordwrap by passing in 0. 238 | * 239 | * @access public 240 | * @return string 241 | * @param array $array PHP array 242 | * @param int $indent Pass in false to use the default, which is 2 243 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) 244 | */ 245 | public function dump($array,$indent = false,$wordwrap = false, $no_opening_dashes = false) { 246 | // Dumps to some very clean YAML. We'll have to add some more features 247 | // and options soon. And better support for folding. 248 | 249 | // New features and options. 250 | if ($indent === false or !is_numeric($indent)) { 251 | $this->_dumpIndent = 2; 252 | } else { 253 | $this->_dumpIndent = $indent; 254 | } 255 | 256 | if ($wordwrap === false or !is_numeric($wordwrap)) { 257 | $this->_dumpWordWrap = 40; 258 | } else { 259 | $this->_dumpWordWrap = $wordwrap; 260 | } 261 | 262 | // New YAML document 263 | $string = ""; 264 | if (!$no_opening_dashes) $string = "---\n"; 265 | 266 | // Start at the base of the array and move through it. 267 | if ($array) { 268 | $array = (array)$array; 269 | $previous_key = -1; 270 | foreach ($array as $key => $value) { 271 | if (!isset($first_key)) $first_key = $key; 272 | $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array); 273 | $previous_key = $key; 274 | } 275 | } 276 | return $string; 277 | } 278 | 279 | /** 280 | * Attempts to convert a key / value array item to YAML 281 | * @access private 282 | * @return string 283 | * @param $key The name of the key 284 | * @param $value The value of the item 285 | * @param $indent The indent of the current node 286 | */ 287 | private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) { 288 | if(is_object($value)) $value = (array)$value; 289 | if (is_array($value)) { 290 | if (empty ($value)) 291 | return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array); 292 | // It has children. What to do? 293 | // Make it the right kind of item 294 | $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array); 295 | // Add the indent 296 | $indent += $this->_dumpIndent; 297 | // Yamlize the array 298 | $string .= $this->_yamlizeArray($value,$indent); 299 | } elseif (!is_array($value)) { 300 | // It doesn't have children. Yip. 301 | $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array); 302 | } 303 | return $string; 304 | } 305 | 306 | /** 307 | * Attempts to convert an array to YAML 308 | * @access private 309 | * @return string 310 | * @param $array The array you want to convert 311 | * @param $indent The indent of the current level 312 | */ 313 | private function _yamlizeArray($array,$indent) { 314 | if (is_array($array)) { 315 | $string = ''; 316 | $previous_key = -1; 317 | foreach ($array as $key => $value) { 318 | if (!isset($first_key)) $first_key = $key; 319 | $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array); 320 | $previous_key = $key; 321 | } 322 | return $string; 323 | } else { 324 | return false; 325 | } 326 | } 327 | 328 | /** 329 | * Returns YAML from a key and a value 330 | * @access private 331 | * @return string 332 | * @param $key The name of the key 333 | * @param $value The value of the item 334 | * @param $indent The indent of the current node 335 | */ 336 | private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) { 337 | // do some folding here, for blocks 338 | if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || 339 | strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '%') !== false || strpos ($value, ' ') !== false || 340 | strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 || 341 | substr ($value, -1, 1) == ':') 342 | ) { 343 | $value = $this->_doLiteralBlock($value,$indent); 344 | } else { 345 | $value = $this->_doFolding($value,$indent); 346 | } 347 | 348 | if ($value === array()) $value = '[ ]'; 349 | if ($value === "") $value = '""'; 350 | if (self::isTranslationWord($value)) { 351 | $value = $this->_doLiteralBlock($value, $indent); 352 | } 353 | if (trim ($value) != $value) 354 | $value = $this->_doLiteralBlock($value,$indent); 355 | 356 | if (is_bool($value)) { 357 | $value = $value ? "true" : "false"; 358 | } 359 | 360 | if ($value === null) $value = 'null'; 361 | if ($value === "'" . self::REMPTY . "'") $value = null; 362 | 363 | $spaces = str_repeat(' ',$indent); 364 | 365 | //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { 366 | if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) { 367 | // It's a sequence 368 | $string = $spaces.'- '.$value."\n"; 369 | } else { 370 | // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); 371 | // It's mapped 372 | if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; } 373 | $string = rtrim ($spaces.$key.': '.$value)."\n"; 374 | } 375 | return $string; 376 | } 377 | 378 | /** 379 | * Creates a literal block for dumping 380 | * @access private 381 | * @return string 382 | * @param $value 383 | * @param $indent int The value of the indent 384 | */ 385 | private function _doLiteralBlock($value,$indent) { 386 | if ($value === "\n") return '\n'; 387 | if (strpos($value, "\n") === false && strpos($value, "'") === false) { 388 | return sprintf ("'%s'", $value); 389 | } 390 | if (strpos($value, "\n") === false && strpos($value, '"') === false) { 391 | return sprintf ('"%s"', $value); 392 | } 393 | $exploded = explode("\n",$value); 394 | $newValue = '|'; 395 | if (isset($exploded[0]) && ($exploded[0] == "|" || $exploded[0] == "|-" || $exploded[0] == ">")) { 396 | $newValue = $exploded[0]; 397 | unset($exploded[0]); 398 | } 399 | $indent += $this->_dumpIndent; 400 | $spaces = str_repeat(' ',$indent); 401 | foreach ($exploded as $line) { 402 | $line = trim($line); 403 | if (strpos($line, '"') === 0 && strrpos($line, '"') == (strlen($line)-1) || strpos($line, "'") === 0 && strrpos($line, "'") == (strlen($line)-1)) { 404 | $line = substr($line, 1, -1); 405 | } 406 | $newValue .= "\n" . $spaces . ($line); 407 | } 408 | return $newValue; 409 | } 410 | 411 | /** 412 | * Folds a string of text, if necessary 413 | * @access private 414 | * @return string 415 | * @param $value The string you wish to fold 416 | */ 417 | private function _doFolding($value,$indent) { 418 | // Don't do anything if wordwrap is set to 0 419 | 420 | if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { 421 | $indent += $this->_dumpIndent; 422 | $indent = str_repeat(' ',$indent); 423 | $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); 424 | $value = ">\n".$indent.$wrapped; 425 | } else { 426 | if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY) 427 | $value = '"' . $value . '"'; 428 | if (is_numeric($value) && is_string($value)) 429 | $value = '"' . $value . '"'; 430 | } 431 | 432 | 433 | return $value; 434 | } 435 | 436 | private function isTrueWord($value) { 437 | $words = self::getTranslations(array('true', 'on', 'yes', 'y')); 438 | return in_array($value, $words, true); 439 | } 440 | 441 | private function isFalseWord($value) { 442 | $words = self::getTranslations(array('false', 'off', 'no', 'n')); 443 | return in_array($value, $words, true); 444 | } 445 | 446 | private function isNullWord($value) { 447 | $words = self::getTranslations(array('null', '~')); 448 | return in_array($value, $words, true); 449 | } 450 | 451 | private function isTranslationWord($value) { 452 | return ( 453 | self::isTrueWord($value) || 454 | self::isFalseWord($value) || 455 | self::isNullWord($value) 456 | ); 457 | } 458 | 459 | /** 460 | * Coerce a string into a native type 461 | * Reference: http://yaml.org/type/bool.html 462 | * TODO: Use only words from the YAML spec. 463 | * @access private 464 | * @param $value The value to coerce 465 | */ 466 | private function coerceValue(&$value) { 467 | if (self::isTrueWord($value)) { 468 | $value = true; 469 | } else if (self::isFalseWord($value)) { 470 | $value = false; 471 | } else if (self::isNullWord($value)) { 472 | $value = null; 473 | } 474 | } 475 | 476 | /** 477 | * Given a set of words, perform the appropriate translations on them to 478 | * match the YAML 1.1 specification for type coercing. 479 | * @param $words The words to translate 480 | * @access private 481 | */ 482 | private static function getTranslations(array $words) { 483 | $result = array(); 484 | foreach ($words as $i) { 485 | $result = array_merge($result, array(ucfirst($i), strtoupper($i), strtolower($i))); 486 | } 487 | return $result; 488 | } 489 | 490 | // LOADING FUNCTIONS 491 | 492 | private function _load($input) { 493 | $Source = $this->loadFromSource($input); 494 | return $this->loadWithSource($Source); 495 | } 496 | 497 | private function _loadString($input) { 498 | $Source = $this->loadFromString($input); 499 | return $this->loadWithSource($Source); 500 | } 501 | 502 | private function loadWithSource($Source) { 503 | if (empty ($Source)) return array(); 504 | if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { 505 | $array = syck_load (implode ("\n", $Source)); 506 | return is_array($array) ? $array : array(); 507 | } 508 | 509 | $this->path = array(); 510 | $this->result = array(); 511 | 512 | $cnt = count($Source); 513 | for ($i = 0; $i < $cnt; $i++) { 514 | $line = $Source[$i]; 515 | 516 | $this->indent = strlen($line) - strlen(ltrim($line)); 517 | $tempPath = $this->getParentPathByIndent($this->indent); 518 | $line = self::stripIndent($line, $this->indent); 519 | if (self::isComment($line)) continue; 520 | if (self::isEmpty($line)) continue; 521 | $this->path = $tempPath; 522 | 523 | $literalBlockStyle = self::startsLiteralBlock($line); 524 | if ($literalBlockStyle) { 525 | $line = rtrim ($line, $literalBlockStyle . " \n"); 526 | $literalBlock = ''; 527 | $line .= ' '.$this->LiteralPlaceHolder; 528 | $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1])); 529 | while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { 530 | $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent); 531 | } 532 | $i--; 533 | } 534 | 535 | // Strip out comments 536 | if (strpos ($line, '#')) { 537 | $line = preg_replace('/\s*#([^"\']+)$/','',$line); 538 | } 539 | 540 | while (++$i < $cnt && self::greedilyNeedNextLine($line)) { 541 | $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); 542 | } 543 | $i--; 544 | 545 | $lineArray = $this->_parseLine($line); 546 | 547 | if ($literalBlockStyle) 548 | $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); 549 | 550 | $this->addArray($lineArray, $this->indent); 551 | 552 | foreach ($this->delayedPath as $indent => $delayedPath) 553 | $this->path[$indent] = $delayedPath; 554 | 555 | $this->delayedPath = array(); 556 | 557 | } 558 | return $this->result; 559 | } 560 | 561 | private function loadFromSource ($input) { 562 | if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) 563 | $input = file_get_contents($input); 564 | 565 | return $this->loadFromString($input); 566 | } 567 | 568 | private function loadFromString ($input) { 569 | $lines = explode("\n",$input); 570 | foreach ($lines as $k => $_) { 571 | $lines[$k] = rtrim ($_, "\r"); 572 | } 573 | return $lines; 574 | } 575 | 576 | /** 577 | * Parses YAML code and returns an array for a node 578 | * @access private 579 | * @return array 580 | * @param string $line A line from the YAML file 581 | */ 582 | private function _parseLine($line) { 583 | if (!$line) return array(); 584 | $line = trim($line); 585 | if (!$line) return array(); 586 | 587 | $array = array(); 588 | 589 | $group = $this->nodeContainsGroup($line); 590 | if ($group) { 591 | $this->addGroup($line, $group); 592 | $line = $this->stripGroup ($line, $group); 593 | } 594 | 595 | if ($this->startsMappedSequence($line)) { 596 | return $this->returnMappedSequence($line); 597 | } 598 | 599 | if ($this->startsMappedValue($line)) { 600 | return $this->returnMappedValue($line); 601 | } 602 | 603 | if ($this->isArrayElement($line)) 604 | return $this->returnArrayElement($line); 605 | 606 | if ($this->isPlainArray($line)) 607 | return $this->returnPlainArray($line); 608 | 609 | return $this->returnKeyValuePair($line); 610 | 611 | } 612 | 613 | /** 614 | * Finds the type of the passed value, returns the value as the new type. 615 | * @access private 616 | * @param string $value 617 | * @return mixed 618 | */ 619 | private function _toType($value) { 620 | if ($value === '') return ""; 621 | 622 | if ($this->setting_empty_hash_as_object && $value === '{}') { 623 | return new stdClass(); 624 | } 625 | 626 | $first_character = $value[0]; 627 | $last_character = substr($value, -1, 1); 628 | 629 | $is_quoted = false; 630 | do { 631 | if (!$value) break; 632 | if ($first_character != '"' && $first_character != "'") break; 633 | if ($last_character != '"' && $last_character != "'") break; 634 | $is_quoted = true; 635 | } while (0); 636 | 637 | if ($is_quoted) { 638 | $value = str_replace('\n', "\n", $value); 639 | if ($first_character == "'") 640 | return strtr(substr ($value, 1, -1), array ('\'\'' => '\'', '\\\''=> '\'')); 641 | return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\\\''=> '\'')); 642 | } 643 | 644 | if (strpos($value, ' #') !== false && !$is_quoted) 645 | $value = preg_replace('/\s+#(.+)$/','',$value); 646 | 647 | if ($first_character == '[' && $last_character == ']') { 648 | // Take out strings sequences and mappings 649 | $innerValue = trim(substr ($value, 1, -1)); 650 | if ($innerValue === '') return array(); 651 | $explode = $this->_inlineEscape($innerValue); 652 | // Propagate value array 653 | $value = array(); 654 | foreach ($explode as $v) { 655 | $value[] = $this->_toType($v); 656 | } 657 | return $value; 658 | } 659 | 660 | if (strpos($value,': ')!==false && $first_character != '{') { 661 | $array = explode(': ',$value); 662 | $key = trim($array[0]); 663 | array_shift($array); 664 | $value = trim(implode(': ',$array)); 665 | $value = $this->_toType($value); 666 | return array($key => $value); 667 | } 668 | 669 | if ($first_character == '{' && $last_character == '}') { 670 | $innerValue = trim(substr ($value, 1, -1)); 671 | if ($innerValue === '') return array(); 672 | // Inline Mapping 673 | // Take out strings sequences and mappings 674 | $explode = $this->_inlineEscape($innerValue); 675 | // Propagate value array 676 | $array = array(); 677 | foreach ($explode as $v) { 678 | $SubArr = $this->_toType($v); 679 | if (empty($SubArr)) continue; 680 | if (is_array ($SubArr)) { 681 | $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; 682 | } 683 | $array[] = $SubArr; 684 | } 685 | return $array; 686 | } 687 | 688 | if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { 689 | return null; 690 | } 691 | 692 | if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){ 693 | $intvalue = (int)$value; 694 | if ($intvalue != PHP_INT_MAX && $intvalue != ~PHP_INT_MAX) 695 | $value = $intvalue; 696 | return $value; 697 | } 698 | 699 | if ( is_string($value) && preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) { 700 | // Hexadecimal value. 701 | return hexdec($value); 702 | } 703 | 704 | $this->coerceValue($value); 705 | 706 | if (is_numeric($value)) { 707 | if ($value === '0') return 0; 708 | if (rtrim ($value, 0) === $value) 709 | $value = (float)$value; 710 | return $value; 711 | } 712 | 713 | return $value; 714 | } 715 | 716 | /** 717 | * Used in inlines to check for more inlines or quoted strings 718 | * @access private 719 | * @return array 720 | */ 721 | private function _inlineEscape($inline) { 722 | // There's gotta be a cleaner way to do this... 723 | // While pure sequences seem to be nesting just fine, 724 | // pure mappings and mappings with sequences inside can't go very 725 | // deep. This needs to be fixed. 726 | 727 | $seqs = array(); 728 | $maps = array(); 729 | $saved_strings = array(); 730 | $saved_empties = array(); 731 | 732 | // Check for empty strings 733 | $regex = '/("")|(\'\')/'; 734 | if (preg_match_all($regex,$inline,$strings)) { 735 | $saved_empties = $strings[0]; 736 | $inline = preg_replace($regex,'YAMLEmpty',$inline); 737 | } 738 | unset($regex); 739 | 740 | // Check for strings 741 | $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; 742 | if (preg_match_all($regex,$inline,$strings)) { 743 | $saved_strings = $strings[0]; 744 | $inline = preg_replace($regex,'YAMLString',$inline); 745 | } 746 | unset($regex); 747 | 748 | // echo $inline; 749 | 750 | $i = 0; 751 | do { 752 | 753 | // Check for sequences 754 | while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { 755 | $seqs[] = $matchseqs[0]; 756 | $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); 757 | } 758 | 759 | // Check for mappings 760 | while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { 761 | $maps[] = $matchmaps[0]; 762 | $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); 763 | } 764 | 765 | if ($i++ >= 10) break; 766 | 767 | } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); 768 | 769 | $explode = explode(',',$inline); 770 | $explode = array_map('trim', $explode); 771 | $stringi = 0; $i = 0; 772 | 773 | while (1) { 774 | 775 | // Re-add the sequences 776 | if (!empty($seqs)) { 777 | foreach ($explode as $key => $value) { 778 | if (strpos($value,'YAMLSeq') !== false) { 779 | foreach ($seqs as $seqk => $seq) { 780 | $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); 781 | $value = $explode[$key]; 782 | } 783 | } 784 | } 785 | } 786 | 787 | // Re-add the mappings 788 | if (!empty($maps)) { 789 | foreach ($explode as $key => $value) { 790 | if (strpos($value,'YAMLMap') !== false) { 791 | foreach ($maps as $mapk => $map) { 792 | $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); 793 | $value = $explode[$key]; 794 | } 795 | } 796 | } 797 | } 798 | 799 | 800 | // Re-add the strings 801 | if (!empty($saved_strings)) { 802 | foreach ($explode as $key => $value) { 803 | while (strpos($value,'YAMLString') !== false) { 804 | $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); 805 | unset($saved_strings[$stringi]); 806 | ++$stringi; 807 | $value = $explode[$key]; 808 | } 809 | } 810 | } 811 | 812 | 813 | // Re-add the empties 814 | if (!empty($saved_empties)) { 815 | foreach ($explode as $key => $value) { 816 | while (strpos($value,'YAMLEmpty') !== false) { 817 | $explode[$key] = preg_replace('/YAMLEmpty/', '', $value, 1); 818 | $value = $explode[$key]; 819 | } 820 | } 821 | } 822 | 823 | $finished = true; 824 | foreach ($explode as $key => $value) { 825 | if (strpos($value,'YAMLSeq') !== false) { 826 | $finished = false; break; 827 | } 828 | if (strpos($value,'YAMLMap') !== false) { 829 | $finished = false; break; 830 | } 831 | if (strpos($value,'YAMLString') !== false) { 832 | $finished = false; break; 833 | } 834 | if (strpos($value,'YAMLEmpty') !== false) { 835 | $finished = false; break; 836 | } 837 | } 838 | if ($finished) break; 839 | 840 | $i++; 841 | if ($i > 10) 842 | break; // Prevent infinite loops. 843 | } 844 | 845 | 846 | return $explode; 847 | } 848 | 849 | private function literalBlockContinues ($line, $lineIndent) { 850 | if (!trim($line)) return true; 851 | if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; 852 | return false; 853 | } 854 | 855 | private function referenceContentsByAlias ($alias) { 856 | do { 857 | if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } 858 | $groupPath = $this->SavedGroups[$alias]; 859 | $value = $this->result; 860 | foreach ($groupPath as $k) { 861 | $value = $value[$k]; 862 | } 863 | } while (false); 864 | return $value; 865 | } 866 | 867 | private function addArrayInline ($array, $indent) { 868 | $CommonGroupPath = $this->path; 869 | if (empty ($array)) return false; 870 | 871 | foreach ($array as $k => $_) { 872 | $this->addArray(array($k => $_), $indent); 873 | $this->path = $CommonGroupPath; 874 | } 875 | return true; 876 | } 877 | 878 | private function addArray ($incoming_data, $incoming_indent) { 879 | 880 | // print_r ($incoming_data); 881 | 882 | if (count ($incoming_data) > 1) 883 | return $this->addArrayInline ($incoming_data, $incoming_indent); 884 | 885 | $key = key ($incoming_data); 886 | $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; 887 | if ($key === '__!YAMLZero') $key = '0'; 888 | 889 | if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. 890 | if ($key || $key === '' || $key === '0') { 891 | $this->result[$key] = $value; 892 | } else { 893 | $this->result[] = $value; end ($this->result); $key = key ($this->result); 894 | } 895 | $this->path[$incoming_indent] = $key; 896 | return; 897 | } 898 | 899 | 900 | 901 | $history = array(); 902 | // Unfolding inner array tree. 903 | $history[] = $_arr = $this->result; 904 | foreach ($this->path as $k) { 905 | $history[] = $_arr = $_arr[$k]; 906 | } 907 | 908 | if ($this->_containsGroupAlias) { 909 | $value = $this->referenceContentsByAlias($this->_containsGroupAlias); 910 | $this->_containsGroupAlias = false; 911 | } 912 | 913 | 914 | // Adding string or numeric key to the innermost level or $this->arr. 915 | if (is_string($key) && $key == '<<') { 916 | if (!is_array ($_arr)) { $_arr = array (); } 917 | 918 | $_arr = array_merge ($_arr, $value); 919 | } else if ($key || $key === '' || $key === '0') { 920 | if (!is_array ($_arr)) 921 | $_arr = array ($key=>$value); 922 | else 923 | $_arr[$key] = $value; 924 | } else { 925 | if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } 926 | else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } 927 | } 928 | 929 | $reverse_path = array_reverse($this->path); 930 | $reverse_history = array_reverse ($history); 931 | $reverse_history[0] = $_arr; 932 | $cnt = count($reverse_history) - 1; 933 | for ($i = 0; $i < $cnt; $i++) { 934 | $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; 935 | } 936 | $this->result = $reverse_history[$cnt]; 937 | 938 | $this->path[$incoming_indent] = $key; 939 | 940 | if ($this->_containsGroupAnchor) { 941 | $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; 942 | if (is_array ($value)) { 943 | $k = key ($value); 944 | if (!is_int ($k)) { 945 | $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; 946 | } 947 | } 948 | $this->_containsGroupAnchor = false; 949 | } 950 | 951 | } 952 | 953 | private static function startsLiteralBlock ($line) { 954 | $lastChar = substr (trim($line), -1); 955 | if ($lastChar != '>' && $lastChar != '|') return false; 956 | if ($lastChar == '|') return $lastChar; 957 | // HTML tags should not be counted as literal blocks. 958 | if (preg_match ('#<.*?>$#', $line)) return false; 959 | return $lastChar; 960 | } 961 | 962 | private static function greedilyNeedNextLine($line) { 963 | $line = trim ($line); 964 | if (!strlen($line)) return false; 965 | if (substr ($line, -1, 1) == ']') return false; 966 | if ($line[0] == '[') return true; 967 | if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; 968 | return false; 969 | } 970 | 971 | private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) { 972 | $line = self::stripIndent($line, $indent); 973 | if ($literalBlockStyle !== '|') { 974 | $line = self::stripIndent($line); 975 | } 976 | $line = rtrim ($line, "\r\n\t ") . "\n"; 977 | if ($literalBlockStyle == '|') { 978 | return $literalBlock . $line; 979 | } 980 | if (strlen($line) == 0) 981 | return rtrim($literalBlock, ' ') . "\n"; 982 | if ($line == "\n" && $literalBlockStyle == '>') { 983 | return rtrim ($literalBlock, " \t") . "\n"; 984 | } 985 | if ($line != "\n") 986 | $line = trim ($line, "\r\n ") . " "; 987 | return $literalBlock . $line; 988 | } 989 | 990 | function revertLiteralPlaceHolder ($lineArray, $literalBlock) { 991 | foreach ($lineArray as $k => $_) { 992 | if (is_array($_)) 993 | $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); 994 | else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) 995 | $lineArray[$k] = rtrim ($literalBlock, " \r\n"); 996 | } 997 | return $lineArray; 998 | } 999 | 1000 | private static function stripIndent ($line, $indent = -1) { 1001 | if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); 1002 | return substr ($line, $indent); 1003 | } 1004 | 1005 | private function getParentPathByIndent ($indent) { 1006 | if ($indent == 0) return array(); 1007 | $linePath = $this->path; 1008 | do { 1009 | end($linePath); $lastIndentInParentPath = key($linePath); 1010 | if ($indent <= $lastIndentInParentPath) array_pop ($linePath); 1011 | } while ($indent <= $lastIndentInParentPath); 1012 | return $linePath; 1013 | } 1014 | 1015 | 1016 | private function clearBiggerPathValues ($indent) { 1017 | 1018 | 1019 | if ($indent == 0) $this->path = array(); 1020 | if (empty ($this->path)) return true; 1021 | 1022 | foreach ($this->path as $k => $_) { 1023 | if ($k > $indent) unset ($this->path[$k]); 1024 | } 1025 | 1026 | return true; 1027 | } 1028 | 1029 | 1030 | private static function isComment ($line) { 1031 | if (!$line) return false; 1032 | if ($line[0] == '#') return true; 1033 | if (trim($line, " \r\n\t") == '---') return true; 1034 | return false; 1035 | } 1036 | 1037 | private static function isEmpty ($line) { 1038 | return (trim ($line) === ''); 1039 | } 1040 | 1041 | 1042 | private function isArrayElement ($line) { 1043 | if (!$line || !is_scalar($line)) return false; 1044 | if (substr($line, 0, 2) != '- ') return false; 1045 | if (strlen ($line) > 3) 1046 | if (substr($line,0,3) == '---') return false; 1047 | 1048 | return true; 1049 | } 1050 | 1051 | private function isHashElement ($line) { 1052 | return strpos($line, ':'); 1053 | } 1054 | 1055 | private function isLiteral ($line) { 1056 | if ($this->isArrayElement($line)) return false; 1057 | if ($this->isHashElement($line)) return false; 1058 | return true; 1059 | } 1060 | 1061 | 1062 | private static function unquote ($value) { 1063 | if (!$value) return $value; 1064 | if (!is_string($value)) return $value; 1065 | if ($value[0] == '\'') return trim ($value, '\''); 1066 | if ($value[0] == '"') return trim ($value, '"'); 1067 | return $value; 1068 | } 1069 | 1070 | private function startsMappedSequence ($line) { 1071 | return (substr($line, 0, 2) == '- ' && substr ($line, -1, 1) == ':'); 1072 | } 1073 | 1074 | private function returnMappedSequence ($line) { 1075 | $array = array(); 1076 | $key = self::unquote(trim(substr($line,1,-1))); 1077 | $array[$key] = array(); 1078 | $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); 1079 | return array($array); 1080 | } 1081 | 1082 | private function checkKeysInValue($value) { 1083 | if (strchr('[{"\'', $value[0]) === false) { 1084 | if (strchr($value, ': ') !== false) { 1085 | throw new Exception('Too many keys: '.$value); 1086 | } 1087 | } 1088 | } 1089 | 1090 | private function returnMappedValue ($line) { 1091 | $this->checkKeysInValue($line); 1092 | $array = array(); 1093 | $key = self::unquote (trim(substr($line,0,-1))); 1094 | $array[$key] = ''; 1095 | return $array; 1096 | } 1097 | 1098 | private function startsMappedValue ($line) { 1099 | return (substr ($line, -1, 1) == ':'); 1100 | } 1101 | 1102 | private function isPlainArray ($line) { 1103 | return ($line[0] == '[' && substr ($line, -1, 1) == ']'); 1104 | } 1105 | 1106 | private function returnPlainArray ($line) { 1107 | return $this->_toType($line); 1108 | } 1109 | 1110 | private function returnKeyValuePair ($line) { 1111 | $array = array(); 1112 | $key = ''; 1113 | if (strpos ($line, ': ')) { 1114 | // It's a key/value pair most likely 1115 | // If the key is in double quotes pull it out 1116 | if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { 1117 | $value = trim(str_replace($matches[1],'',$line)); 1118 | $key = $matches[2]; 1119 | } else { 1120 | // Do some guesswork as to the key and the value 1121 | $explode = explode(': ', $line); 1122 | $key = trim(array_shift($explode)); 1123 | $value = trim(implode(': ', $explode)); 1124 | $this->checkKeysInValue($value); 1125 | } 1126 | // Set the type of the value. Int, string, etc 1127 | $value = $this->_toType($value); 1128 | 1129 | if ($key === '0') $key = '__!YAMLZero'; 1130 | $array[$key] = $value; 1131 | } else { 1132 | $array = array ($line); 1133 | } 1134 | return $array; 1135 | 1136 | } 1137 | 1138 | 1139 | private function returnArrayElement ($line) { 1140 | if (strlen($line) <= 1) return array(array()); // Weird %) 1141 | $array = array(); 1142 | $value = trim(substr($line,1)); 1143 | $value = $this->_toType($value); 1144 | if ($this->isArrayElement($value)) { 1145 | $value = $this->returnArrayElement($value); 1146 | } 1147 | $array[] = $value; 1148 | return $array; 1149 | } 1150 | 1151 | 1152 | private function nodeContainsGroup ($line) { 1153 | $symbolsForReference = 'A-z0-9_\-'; 1154 | if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) 1155 | if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; 1156 | if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; 1157 | if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; 1158 | if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; 1159 | if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; 1160 | return false; 1161 | 1162 | } 1163 | 1164 | private function addGroup ($line, $group) { 1165 | if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); 1166 | if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); 1167 | //print_r ($this->path); 1168 | } 1169 | 1170 | private function stripGroup ($line, $group) { 1171 | $line = trim(str_replace($group, '', $line)); 1172 | return $line; 1173 | } 1174 | } 1175 | } 1176 | 1177 | // Enable use of Spyc from command line 1178 | // The syntax is the following: php Spyc.php spyc.yaml 1179 | 1180 | do { 1181 | if (PHP_SAPI != 'cli') break; 1182 | if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; 1183 | if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'Spyc.php') ) break; 1184 | $file = $argv[1]; 1185 | echo json_encode (spyc_load_file ($file)); 1186 | } while (0); --------------------------------------------------------------------------------