diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..efcbeb1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.6 + +before_script: + - cd test + - composer self-update || true + - composer install --no-interaction --no-progress + +script: + - php check.php diff --git a/README.md b/README.md index c025622..a31dd04 100644 --- a/README.md +++ b/README.md @@ -509,7 +509,7 @@ A curated list of amazingly awesome PHP libraries, resources and shiny things. * [Ruler](https://github.com/bobthecow/Ruler) - A simple stateless production rules engine. * [LiteCQRS](https://github.com/beberlei/litecqrs-php) - A CQRS (Command Query Responsibility Separation) library. * [Sslurp](https://github.com/EvanDotPro/Sslurp) - A library that makes dealing with SSL suck less. -* [PHP Option](https://github.com/schmittjoh/php-option) An option type library. +* [PHP Option](https://github.com/schmittjoh/php-option) - An option type library. * [Metrics](https://github.com/beberlei/metrics) - A simple metrics API library. * [Sabre VObject](https://github.com/evert/sabre-vobject) - A library for parsing VCard and iCalendar objects. * [Annotations](https://github.com/doctrine/annotations) - An annotations library (part of Doctrine). @@ -559,7 +559,7 @@ A curated list of amazingly awesome PHP libraries, resources and shiny things. * [Ansible](http://www.ansibleworks.com/) - A radically simple orchestration framework. * [Puppet](http://puppetlabs.com/) - A server automation framework and application. * [Chef](https://github.com/opscode/chef) - A systems integration framework. -* [SaltStack](http://saltstack.com/community.html) - An infrastructure management tool. +* [SaltStack](http://www.saltstack.com/) - An infrastructure management tool. * [PHP Brew](https://github.com/c9s/phpbrew) - A PHP version manager and installer. * [PHP Env](https://github.com/CHH/phpenv) - Another PHP version manager. * [PHP Switch](https://github.com/jubianchi/phpswitch) - Another version manager. @@ -630,7 +630,6 @@ Various resources, such as books, websites and articles, for improving your PHP * [Preventing CSRF Attacks](http://blog.ircmaxell.com/2013/02/preventing-csrf-attacks.html) - An article on preventing CSRF attacks. * [Don't Worry About BREACH](http://blog.ircmaxell.com/2013/08/dont-worry-about-breach.html) - An article about the BREACH hack and CSRF tokens. * [On PHP 5.3, Lamda Functions and Closures](http://fabien.potencier.org/article/17/on-php-5-3-lambda-functions-and-closures) - An article about lambda functions and closures. -* [Use Env](http://seancoates.com/blogs/use-env) - An article about using the unix environment helper. * [Composer Primer](http://daylerees.com/composer-primer) - A Composer primer. * [Composer Versioning](https://igor.io/2013/01/07/composer-versioning.html) - An article about Composer versioning. * [Composer Stability Flags](https://igor.io/2013/02/07/composer-stability-flags.html) - An article about Composer stability flags. diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..4f4acd3 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +vendor/ +composer.lock \ No newline at end of file diff --git a/test/check.php b/test/check.php new file mode 100644 index 0000000..1ba9cca --- /dev/null +++ b/test/check.php @@ -0,0 +1,188 @@ +identifyLine($lines, $i); + + // call consume method for the detected block type to consume further lines + list($block, $i) = $this->{'consume' . $blockType}($lines, $i); + if ($block !== false) { + $blocks[] = $block; + } + } + } + + return $blocks; + } +} + +// Loading Markdown and parsing blocks from it. +$text = file_get_contents(__DIR__ . '/../README.md'); +$markdown = new MyMarkdown(); +$blocks = $markdown->parse($text); + +// Run checks. +checkBlocks($blocks); +checkLinks($text); + +// Some List items have a special formatting. +// List items that are stripos'ing one of these strings will be ignored. +function getIgnoredListItems() +{ + return array( + 'blog.ircmaxell.com/2013/09/beyond-design-patterns', + 'blog.ircmaxell.com/2012/03/phps-source-code-for-php-developers', + 'php.net/manual/en/features.gc.refcounting-basics', + ); +} + +//--- Functions + +function checkLinks($text) +{ + // Regex from: https://mathiasbynens.be/demo/url-regex + preg_match_all('_\((?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s\)]*)?\)_iuS', $text, $links); + $links = array_map(function ($link) { + return trim($link, '()'); + }, array_unique($links[0])); + + $client = new GuzzleHttp\Client(); + + // Ignoring some domains. + $links = array_filter($links, function($link) { + $ignoredDomains = array( + 'www.owasp.org', // Their server somehow fails responding to curl. + ); + foreach($ignoredDomains as $ignoredDomain) { + if(false !== stripos($link, $ignoredDomain)) { + return false; + } + } + return true; + }); + + // Creating guzzle requests. + $requests = array_map(function ($link) use ($client) { + return $client->createRequest('GET', $link, array('verify' => false, 'timeout' => 99999)); + }, $links); + + // Start all these requests partially parallel. + $responses = GuzzleHttp\batch( + $client, + $requests, + array( + 'parallel' => 5, + 'complete' => function (\GuzzleHttp\Event\CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + }, + 'error' => function (\GuzzleHttp\Event\ErrorEvent $event) { + echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n"; + $event->throwImmediately(true); + } + ) + ); + + // Check for exceptions. + foreach($responses as $response) { + if($response instanceof \Exception) { + throw $response; + } + } +} + +function checkBlocks(array $blocks) +{ + // Check List formatting + $lists = onlyLists($blocks); + $tableOfContent = array_shift($lists); + checkLists($lists); + + // Check table of content ordering. + $tableOfContent = $tableOfContent['items']; + $indentedHeadlines = indentHeadlines(onlyHeadlines($blocks)); + // Checking array to be euqal. + if (var_export($tableOfContent, true) != var_export($indentedHeadlines, true)) { + throw new \Exception("Table of contents and headlines in document are _not_ in the same order."); + } +} + +function indentHeadlines(array $headlines) +{ + $indented = array(); + $index = 0; + foreach ($headlines as $headline) { + $content = $headline['content']; + $contentFormatted = '[' . $content . '](#' . str_ireplace(' ', '-', strtolower($content)) . ')'; + if (1 == $headline['level']) { + $index++; + $indented[$index][] = $contentFormatted; + } else { + $indented[$index][] = '- ' . $contentFormatted; + } + } + + return $indented; +} + +function checkLists(array $lists) +{ + array_map(function ($list) { + if ('list' == $list['type']) { + checkListItems($list['items']); + } + }, $lists); +} + +function checkListItems(array $list) +{ + array_map(function ($items) { + foreach ($items as $item) { + validateListItem($item); + } + }, $list); +} + +function validateListItem($item) +{ + $item = trim($item); + + foreach (getIgnoredListItems() as $ignoredString) { + if (false !== stripos($item, $ignoredString)) { + // ignore this list item because it contains a special formatting or such. + return; + } + } + + if (!preg_match('@^\[(.+)\]\((.+)\) - (.+).$@i', $item, $matches)) { + throw new \Exception("Invalid ListItem: " . $item); + } + //$name = $matches[1]; + //$url = $matches[2]; + //$description = $matches[3]; +} + +function onlyHeadlines(array $blocks) +{ + return array_values(array_filter($blocks, function ($block) { + return 'headline' == $block['type']; + })); +} + +function onlyLists(array $blocks) +{ + return array_values(array_filter($blocks, function ($block) { + return 'list' == $block['type']; + })); +} diff --git a/test/composer.json b/test/composer.json new file mode 100644 index 0000000..17ede4d --- /dev/null +++ b/test/composer.json @@ -0,0 +1,9 @@ +{ + "name": "ziadoz/awesome-php", + "description": "Just a test script for formatting and links.", + "require": { + "cebe/markdown": "~0.9", + "guzzlehttp/guzzle": "~4.0" + }, + "license": "MIT" +}