Initial commit

This commit is contained in:
Ade Attwood 2018-03-26 20:13:25 +01:00
commit eee44b2ada
9 changed files with 3901 additions and 0 deletions

93
.gitignore vendored Normal file
View file

@ -0,0 +1,93 @@
# Created by https://www.gitignore.io/api/vim,macos,linux,windows,composer,phpcodesniffer
### Composer ###
*.phar
/vendor/
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### PHPCodeSniffer ###
# CodeSniffer
/vendor/*
/wpcs/*
### Vim ###
# swap
.sw[a-p]
.*.sw[a-p]
# session
Session.vim
# temporary
.netrwhist
# auto-generated tag files
tags
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.gitignore.io/api/vim,macos,linux,windows,composer,phpcodesniffer

20
RoboFile.php Normal file
View file

@ -0,0 +1,20 @@
<?php
/**
* This is project's console commands configuration for Robo task runner.
*
* @see http://robo.li/
*/
class RoboFile extends \Robo\Tasks
{
// define public methods as commands
/**
* @command my-project:command-one
*/
function hello()
{
$this->io()->title("Build all site assets");
$this->taskExec('ls')->run();
}
}

14
bin/bootstrap.php Normal file
View file

@ -0,0 +1,14 @@
<?php
set_time_limit(0);
$autoLoaders = [
__DIR__.'/../vendor/autoload.php',
__DIR__.'/../../../autoload.php'
];
foreach ($autoLoaders as $loader) {
if (is_file($loader)) {
include_once $loader;
}
}

10
bin/build-phar Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env php
<?php
require __DIR__.'/bootstrap.php';
use pgrep\Compiler;
$compiler = new Compiler;
$compiler->compile();

15
bin/pgrep Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env php
<?php
require __DIR__.'/bootstrap.php';
use pgrep\DefaultCommand;
use Symfony\Component\Console\Application;
$application = new Application('echo', '1.0.0');
$command = new DefaultCommand();
$application->add($command);
$application->setDefaultCommand($command->getName(), true);
$application->run();

32
composer.json Normal file
View file

@ -0,0 +1,32 @@
{
"name": "adeattwood/pgrep",
"require": {
"php": ">=7.0 <8.0",
"symfony/console": "^3.2",
"symfony/finder": "^3.4",
"consolidation/robo": "^1.2"
},
"require-dev": {
"codeception/codeception": "^2.2",
"codeception/specify": "^0.4.6",
"codeception/verify": "^0.3.3",
"squizlabs/php_codesniffer": "^2.8",
"symfony/var-dumper": "^3.2"
},
"authors": [
{
"name": "Ade Attwood",
"email": "code@adeattwood.co.uk",
"homepage": "http://adeattwood.co.uk",
"role": "Developer"
}
],
"autoload": {
"psr-4": {
"pgrep\\": "src/"
}
},
"bin": [
"bin/pgrep"
]
}

3412
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

132
src/Compiler.php Normal file
View file

@ -0,0 +1,132 @@
<?php
namespace pgrep;
use Symfony\Component\Finder\Finder;
class Compiler
{
public $name = 'pgrep.phar';
private $version;
private $branchAliasVersion = '';
private $versionDate;
private $basePath;
public function __construct()
{
$this->basePath = dirname(__DIR__).'/';
}
public function getFinder()
{
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->notName('Compiler.php')
->exclude('Tests')
->exclude('tests')
->exclude('docs')
->in($this->basePath.'src')
->in($this->basePath.'bin')
->in($this->basePath.'vendor/symfony')
->in($this->basePath.'vendor/composer');
return $finder;
}
public function getFiles()
{
return [
$this->basePath.'bin/pgrep',
$this->basePath.'vendor/autoload.php'
];
}
public function compile()
{
echo "Starting\n";
$phar = new \Phar($this->name, 0, $this->name);
$phar->setSignatureAlgorithm(\Phar::SHA1);
$phar->startBuffering();
foreach ($this->getFinder() as $file) {
$this->addFile($phar, $file);
}
foreach ($this->getFiles() as $file) {
$this->addFile($phar, $file);
}
$phar->setStub(<<<EOL
#!/usr/bin/env php
<?php
Phar::mapPhar();
require 'phar://{$this->name}/bin/pgrep';
__HALT_COMPILER();
EOL
);
$phar->stopBuffering();
}
public function addFile($phar, $file)
{
if (is_string($file)) {
$file = new \SplFileInfo($file);
}
$path = str_replace($this->basePath, '', $file->getRealPath());
echo "Adding {$path}\n";
$content = file_get_contents($file->getRealPath());
$content = preg_replace('{^#!/usr/bin/env php\s*}', '', $content);
$content = $this->stripWhitespace($content);
$phar->addFromString($path, $content);
}
/**
* Removes whitespace from a PHP source string while preserving line numbers.
*
* @param string $source A PHP string
*
* @return string The PHP string with the whitespace removed
*/
public function stripWhitespace($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= str_repeat("\n", substr_count($token[1], "\n"));
} elseif (T_WHITESPACE === $token[0]) {
// reduce wide spaces
$whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
// normalize newlines to \n
$whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
// trim leading spaces
$whitespace = preg_replace('{\n +}', "\n", $whitespace);
$output .= $whitespace;
} else {
$output .= $token[1];
}
}
return $output;
}
}

173
src/DefaultCommand.php Normal file
View file

@ -0,0 +1,173 @@
<?php
namespace pgrep;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Exception;
class DefaultCommand extends \Symfony\Component\Console\Command\Command
{
public $count = 0;
public $outputTemplate = '{file}:{lineNumber} {line}';
/**
* Sets the name, help, options and arguments for the command
*
* @return void
*/
protected function configure()
{
$options = [
[
'case-insensitive',
'i',
InputOption::VALUE_NONE,
'Case insensitive regex'
],
[
'count',
null,
InputOption::VALUE_NONE,
'Only print the total number of matches found'
],
[
'in',
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Directory to find files in',
[getcwd()]
],
[
'size',
null,
InputOption::VALUE_OPTIONAL,
'A size range of files to look into'
],
[
'date',
null,
InputOption::VALUE_OPTIONAL,
'Date range to search in files. The date must be something that strtotime()'
],
[
'depth',
null,
InputOption::VALUE_OPTIONAL,
'The depth of directors to dive into'
],
];
$this->setName('pgrep')
->setDescription('Grep with php regex')
->addArgument('pattern', InputArgument::REQUIRED, 'The pattern to look for')
->setHelp('Search for php regex in files');
foreach ($options as $option) {
call_user_func_array([$this, 'addOption'], $option);
}
}
/**
* Runs the command
*
* @param InputInterface $input The interface implemented the symfony input class
* @param OutputInterface $output The interface implemented the symfony output class
*
* @return void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$inputStream = $input->getStream() ?: STDIN;
stream_set_blocking($inputStream, 0);
$pipedInput = stream_get_contents($inputStream);
$pattern = '/'.$input->getArgument('pattern').'/';
if ($input->getOption('case-insensitive') === true) {
$pattern .= 'i';
}
if ($pipedInput !== '') {
$this->outputTemplate = '{line}';
$out = $this->grepContents($pattern, $pipedInput);
if (!$input->getOption('count')) {
$output->writeln($out);
} else {
$output->writeln($this->count);
}
return;
}
foreach ($this->buildFinder($input) as $file) {
try {
$contents = $file->getContents();
} catch (Exception $e) {
continue;
}
$out = $this->grepContents($pattern, $contents, $file);
if (!$input->getOption('count')) {
$output->writeln($out);
}
}
if ($input->getOption('count')) {
$output->writeln($this->count);
}
}
public function grepContents($pattern, $contents, $file = null)
{
$lines = explode("\n", $contents);
preg_match_all($pattern, $contents, $matches, PREG_OFFSET_CAPTURE);
foreach (current($matches) as $match) {
$this->count++;
$lineIndex = substr_count(mb_substr($contents, 0, $match[1]), PHP_EOL);
$line = preg_replace($pattern, '<fg=green;options=bold>$0</>', $lines[$lineIndex]);
return strtr($this->outputTemplate, [
'{matchValue}' => $match[0],
'{file}' => $file ? $file->getPathName() : '',
'{fileRelative}' => $file ? $file->getRelativePathname() : '',
'{line}' => $line,
'{lineNumber}' => $lineIndex + 1,
]);
}
}
public function buildFinder($input)
{
$finder = new Finder();
$finder->ignoreUnreadableDirs()->files();
foreach ($input->getOption('in') as $dir) {
$finder->in($dir);
}
if (($size = $input->getOption('size')) !== null) {
$finder->size($size);
}
if (($date = $input->getOption('date')) !== null) {
$finder->date($date);
}
if (($depth = $input->getOption('depth')) !== null) {
$finder->depth($depth);
}
return $finder;
}
}