Initial commit
This commit is contained in:
commit
eee44b2ada
9 changed files with 3901 additions and 0 deletions
93
.gitignore
vendored
Normal file
93
.gitignore
vendored
Normal 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
20
RoboFile.php
Normal 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
14
bin/bootstrap.php
Normal 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
10
bin/build-phar
Executable 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
15
bin/pgrep
Executable 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
32
composer.json
Normal 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
3412
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
132
src/Compiler.php
Normal file
132
src/Compiler.php
Normal 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
173
src/DefaultCommand.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue