Skip to content

Commit

Permalink
🐛 Fixed bug where autoload.php was not being found correctly
Browse files Browse the repository at this point in the history
♻️ Refactored ProcessDirectory and ProcessFile classes
🔧 Added bin entry to composer.json
✨ Added readme with installation instructions and usage examples
  • Loading branch information
molbal committed Jan 8, 2023
1 parent 9574ebd commit a0d1032
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 44 deletions.
20 changes: 20 additions & 0 deletions bin/aiphpdocs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env php
<?php

if (file_exists(__DIR__.'/../../../autoload.php')) {
require __DIR__.'/../../../autoload.php';
} else {
require __DIR__.'/../vendor/autoload.php';
}

use Molbal\AiPhpdoc\ProcessDirectory;
use Molbal\AiPhpdoc\ProcessFile;
use Symfony\Component\Console\Application;

$application = new Application('AI-PHPDocs by molbal', '1.0.0');

$application->add(new ProcessFile());
$application->add(new ProcessDirectory());

$application->run();

10 changes: 5 additions & 5 deletions bin/aiphpdocs.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?php
require __DIR__.'/../vendor/autoload.php';

use Molbal\AiPhpdoc\InsertMissingDocs;
use Molbal\AiPhpdoc\ProcessDirectory;
use Molbal\AiPhpdoc\ProcessFile;
use Symfony\Component\Console\Application;

$application = new Application();
$application = new Application('AI-PHPDocs by molbal', '1.0.0');


$application->add(new InsertMissingDocs());
// ... register commands
$application->add(new ProcessFile());
$application->add(new ProcessDirectory());

$application->run();

5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
"php": "^8.1",
"symfony/console": "^6.2",
"openai-php/client": "^0.3.0"
}
},
"bin": [
"bin/aiphpdocs"
]
}
47 changes: 47 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# AI PHPDocs

AI PHPDocs is a tool that uses GPT-3 to automatically add missing PHPDoc comments to your PHP code.

## Prerequisites

Before using AI PHPDocs, you will need to have an OpenAI API key set as an environment variable.

```shell
export OPENAI_KEY=...
```

## Installation

To install AI PHPDocs, run the following command:


```shell
composer global require molbal/ai-phpdocs
```

## Usage

To add missing PHPDoc comments to a single file, use the following command:

```shell
aiphpdocs file /path/to/file.php
```

To add missing PHPDoc comments to a directory of files, use the following command. By default it iterates through the current directory for all files, but does not go into subdirectories:

```shell
aiphpdocs directory
```


You may set the `--recursive` flag, or `-r` for short for it to go into subdirectories.

If you pass another variable (regardless of the recursive flag) it will treat it as another directory to sweep through instead of the working directory.

```shell
aiphpdocs directory --r /somewhere/else
```

## License

AI PHPDocs is licensed under the AGPL-3.0 license. See LICENSE for more information.
37 changes: 37 additions & 0 deletions src/ProcessDirectory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Molbal\AiPhpdoc;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ProcessDirectory extends Command
{

protected function configure()
{
$this
->setName('directory')
->setDescription('Adds missing PHPDoc blocks to functions in a directory')
->addArgument('directory', InputArgument::OPTIONAL, 'The directory to iterate through. Defaults to `.`')
->addOption('recursive', 'r', InputOption::VALUE_NONE,'Iterate recusrively? Defaults to no');
}


/**
* Execute the function
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dirPath = $input->getArgument('file');
$recursive = $input->getOption('recursive') !== false;
return (new ProcessFacade())->processDirectory($dirPath, $recursive, $output);
}
}
76 changes: 38 additions & 38 deletions src/InsertMissingDocs.php → src/ProcessFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,39 @@

namespace Molbal\AiPhpdoc;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'aiphpdocs:generate',
description: 'Inserts missing PHPDoc blocks in a file.',
aliases: [],
hidden: false
)]
class InsertMissingDocs extends Command
class ProcessFacade
{


/**
* Configures the current command.
*
* @param string $file The file to list the functions of
*/
protected function configure()
{
$this->addArgument('file', InputArgument::REQUIRED, 'The file to list the functions of');
}


/**
* Execute the function
*
* @param InputInterface $input
* @param mixed $filePath
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
public function processFile(mixed $filePath, OutputInterface $output): int
{
$filePath = $input->getArgument('file');
try {
$functions = FileParser::getFunctionsFromFile($filePath);
$errors = 0;
$completions = 0;
foreach ($functions as $function) {
if (!$function['phpdoc']) {
$output->writeln('Found function without docblock: '.$function['name'].'');
$output->writeln('Found function without docblock: ' . $function['name'] . '');
try {
$docs = DocumentationGenerator::createDocBlock($function['body']);
if (FileWriter::writeDocBlock($filePath, $function['body'], $docs)) {
$output->writeln('<info>Wrote docblock for '.$function['name'].'</info>');
$output->writeln('<info>Wrote docblock for ' . $function['name'] . '</info>');
$completions++;
}
else {
} else {
if (FileWriter::writeDocBlock($filePath, $function['body'], $docs)) {
$output->writeln('<error>Generated docblock for '.$function['name'].', but could not write it to the file.</error>');
$output->writeln('<error>Generated docblock for ' . $function['name'] . ', but could not write it to the file.</error>');
$output->writeln($docs);
$errors++;
}
}
}
catch (\Exception $error) {
$output->writeln('<error>Could not generate docblock for '.$function['name'].': '.$error->getMessage().'</error>');
} catch (\Exception $error) {
$output->writeln('<error>Could not generate docblock for ' . $function['name'] . ': ' . $error->getMessage() . '</error>');
}
}
}
Expand All @@ -71,7 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
}

if ($completions > 0) {
$output->writeln('Finished processing '.$filePath.' with '.$completions.' docblocks written and '.$errors.' errors.');
$output->writeln('Finished processing ' . $filePath . ' with ' . $completions . ' docblocks written and ' . $errors . ' errors.');
}

return $errors > 0 ? Command::FAILURE : Command::SUCCESS;
Expand All @@ -80,4 +53,31 @@ protected function execute(InputInterface $input, OutputInterface $output)
return Command::FAILURE;
}
}
}

public function processDirectory(string $directoryPath, bool $recursive, OutputInterface $output): int
{
$output->writeln('<comment>Processing directory: '.$directoryPath.'</comment>');
$success = Command::SUCCESS;

if (!is_dir($directoryPath)) {
$output->writeln('<error>'.$directoryPath.' is not a valid directory.</error>');
return Command::INVALID;
}

$iterator = new \RecursiveDirectoryIterator($directoryPath, \RecursiveDirectoryIterator::SKIP_DOTS);


foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() == 'php') {
$success = $this->processFile($file->getPathname(), $output) == Command::SUCCESS ? Command::SUCCESS : Command::FAILURE;
}

if ($file->isDir() && $recursive ) {
$this->processDirectory($file->getPathname(), $recursive, $output);
}
}

return $success;
}

}
40 changes: 40 additions & 0 deletions src/ProcessFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Molbal\AiPhpdoc;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ProcessFile extends Command
{

/**
* Configures the current command.
*
* @param string $file The file to list the functions of
*/
protected function configure()
{
$this
->setName('file')
->setDescription('Adds missing PHPDoc blocks to functions in a file')
->addArgument('file', InputArgument::REQUIRED, 'The file to process.');
}


/**
* Execute the function
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$filePath = $input->getArgument('file');
return (new ProcessFacade())->processFile($filePath, $output);
}
}

0 comments on commit a0d1032

Please sign in to comment.