PHP Composer包开发
基于Composer的Laravel扩展包开发工作流
实现laravle项目的文件管理,添加文件/文件夹,删除文件,查看代码/文件(代码支持缩进,支持语法高亮)
composer 项目地址:packagist.org/packages/wuqb/larave...
github 项目地址: github.com/wuxiumu/code-management
1、在项目下执行
mkdir packages // 创建packages 目录 | |
cd packages // 进入 packages 目录 | |
mkdir code-management // 创建code-management 目录,准备开发laravle文件管理 |
项目目录结构
- code-management | |
--config | |
--codemirror-5.31.0 [前端文件包] | |
--codeview.php [配置名称,路由,登录密码的] | |
--routes | |
--codevew.php [codeview 路由地址] | |
--src [开发包] | |
--Faceds | |
--CodeView.php | |
--CodeView.php | |
--CodeViewProvider.php | |
--composer.json |
2、修改composer.json 文件
"autoload-dev": { | |
"psr-4": { | |
"App\\": "app/" | |
} | |
}, |
修改后
"autoload-dev": { | |
"psr-4": { | |
"App\\": "app/", | |
"Wqb\\CodeView\\": "packages/code-management/src" | |
} | |
}, |
3、执行 composer命令
composer dump_autoload
4、添加包的ServiceProvider
由于laravel的 package:discover 是读取 vendor/composer/installed.json 这个文件中安装的包,而我们的包是手动添加,所以无法自动添加ServiceProvider,手动在app.php中添加
'providers' => [ | |
Wqb\CodeView\CodeViewProvider::class | |
], | |
'aliases' => [ | |
'CodeView' => Wqb\CodeView\Facades\CodeView::class | |
], |
上代码
code-management/composer.json
{ | |
"name": "wuqb/laravel-codeview", | |
"description": "laravel codeview", | |
"license": "MIT", | |
"authors": [ | |
{ | |
"name": "wuqb", | |
"email": "824543976@qq.com" | |
} | |
], | |
"autoload": { | |
"psr-4": { | |
"Wqb\\CodeView\\": "src" | |
} | |
}, | |
"require": {} | |
} |
code-management/config/codeview.php
return [ | |
'name' => '简单的线上代码管理工具', | |
'password' => 'tk12', | |
'route' => '/codeview', | |
]; |
code-management/routes/codeview.php
Route::any('codeview','\Wqb\CodeView\CodeView@index'); |
code-management/src/Faceds/CodeView.php
namespace Wqb\CodeView\Facades; | |
use Illuminate\Support\Facades\Facade; | |
class CodeView extends Facade | |
{ | |
protected static function getFacadeAccessor() | |
{ | |
return 'codeview'; | |
} | |
} |
code-management/src/CodeView.php
namespace Wqb\CodeView; | |
use Illuminate\Config\Repository; | |
use Illuminate\Http\Request; | |
class CodeView | |
{ | |
protected $config; | |
/** | |
* 构造方法 | |
*/ | |
public function __construct(Repository $config) | |
{ | |
$this->config = $config->get('codeview'); | |
} | |
/** | |
* 展示页 | |
*/ | |
public function index(Request $request) | |
{ | |
$this->head(); | |
$this->viewAction($request); | |
$this->foot(); | |
} | |
/** | |
* 展示处理 | |
*/ | |
public function viewAction($request) | |
{ | |
$password = $this->config['password']; | |
$n = $this->config['route']; | |
$csrf_token = csrf_token(); | |
if (isset($_COOKIE['filehelper_login_password_123456789']) && $_COOKIE['filehelper_login_password_123456789'] == md5($password)) { | |
$c = $request->input('c'); | |
$v = $request->input('v'); | |
$a = $request->input('a'); | |
$b = $request->input('b'); | |
$v1 = './' . $v; | |
$va = $v1 . $a; | |
echo '<h3>' . $this->config['name'] . '</h3>'; | |
switch ($c) { | |
case 'del': | |
if (is_file($va) == true) { | |
unlink($va); | |
echo "成功删除" . $a . "!"; | |
} else { | |
echo "文件已经被删除!"; | |
} | |
break; | |
case 'up': | |
if ($_FILES["file"]["error"] > 0) { | |
echo "上传失败! 错误码:" . $_FILES["file"]["error"] . "<br>"; | |
} else { | |
if (file_exists($v1 . $_FILES["file"]["name"])) { | |
echo "文件已经存在。 "; | |
} else { | |
move_uploaded_file($_FILES["file"]["tmp_name"], $v1 . $_FILES["file"]["name"]); | |
echo "上传成功!"; | |
} | |
} | |
break; | |
case 'md': | |
if ($a != "") { | |
if (is_dir($va)) { | |
echo "文件夹已存在!"; | |
} else { | |
mkdir($va); | |
echo '文件夹创建成功!'; | |
} | |
} else { | |
echo "文件夹名不能为空!"; | |
} | |
break; | |
case 'ed': | |
if ($request->input('s') == "1") { | |
$eded = fopen($va, 'w'); | |
fwrite($eded, $_POST['fs']); | |
fclose($eded); | |
echo '<h3>保存成功!</h3>'; | |
} else { | |
echo '<a href="' . $n . '?v=' . $v . '">返回</a><br> | |
<form action="' . $n . '?v=' . $v . '&a=' . $a . '&c=ed&s=1" method="post" enctype="multipart/form-data"> | |
<textarea type="text" name="fs" id="ed">' . str_replace('</textarea>', '</ t e xtarea>', file_get_contents($va)) . '</textarea></br> | |
<input type="hidden" name="_token" value="'.$csrf_token.'"> | |
<button type="submit">保存</button></form>'; | |
} | |
break; | |
default: | |
# code... | |
break; | |
} | |
// 编辑页面是否展示列表 | |
if ($b == "1") { | |
} else { | |
echo '<a href="' . $n . '?v=' . dirname($v) . '/">返回</a><br> | |
<table><tr> | |
<th>名称    </th> | |
<th>类型    </th> | |
<th>大小    </th> | |
<th>操作    </th> | |
</tr> | |
'; | |
if (is_dir($v1) == true) { | |
$fs = scandir($v1); | |
$i = 2; | |
while ($i <= count($fs) - 1) { | |
if ($fs[$i] != $n) { | |
echo "<tr><td>" . $fs[$i] . "</td>"; | |
if (is_dir($v1 . $fs[$i]) == true) { | |
echo "<td>文件夹</td>"; | |
echo "<td>-</td>"; | |
echo '<td><a href=' . $n . '?v=' . $v . $fs[$i] . '/>打开</a></td>'; | |
} | |
if (is_file($v1 . $fs[$i]) == true) { | |
echo "<td>文件</td>"; | |
echo "<td>" . number_format(filesize($v1 . $fs[$i]) / 1024 / 1024, 4, ".", "") . "MB</td>"; | |
if (substr(strrchr($fs[$i], '.'), 1) !== 'php' && substr(strrchr($fs[$i], '.'), 1) !== 'asp' && substr(strrchr($fs[$i], '.'), 1) !== 'aspx' && substr(strrchr($fs[$i], '.'), 1) !== 'do') { | |
echo '<td><a href="' . $v . $fs[$i] . '">下载</a>'; | |
} else { | |
echo '<td><a href="' . $v . $fs[$i] . '">打开</a>'; | |
} | |
echo ' <a href="' . $n . '?c=del&a=' . $fs[$i] . '&v=' . $v . '">删除</a>'; | |
$ihzm = substr(strrchr($fs[$i], '.'), 1); | |
if ($ihzm !== 'png' && $ihzm !== 'doc' && $ihzm !== 'docx' && $ihzm !== 'jpg' && $ihzm !== 'gif' && $ihzm !== 'zip' && $ihzm !== 'apk' && $ihzm !== 'webp' && $ihzm !== 'ppt' && $ihzm !== 'pptx' && $ihzm !== 'exe' && $ihzm !== 'xls') { | |
echo ' <a href="' . $n . '?c=ed&v=' . $v . '&a=' . $fs[$i] . '&b=1">编辑</a>'; | |
} | |
echo '</td>'; | |
} | |
echo "</tr>"; | |
} | |
$i++; | |
} | |
} | |
echo '</table><hr> | |
<form action="' . $n . '?v=' . $v . '&c=up" method="post" enctype="multipart/form-data"> | |
上传文件:<input type="file" name="file"> | |
<input type="hidden" name="_token" value="'.$csrf_token.'"> | |
<input type="submit" value="上传"> | |
</form> | |
<form action="' . $n . '" method="get" enctype="multipart/form-data"> | |
<input type="hidden" name="_token" value="'.$csrf_token.'"> | |
新建文件夹:<input type="text" name="a"><input type="hidden" name="c" value="md"><input type="hidden" name="v" value="' . $v . '"> | |
<input type="submit" value="新建"> | |
</form>'; | |
} | |
} else { | |
if ($request->input('password') == $password) { | |
setcookie("filehelper_login_password_123456789", md5($password), time() + 3600); | |
header("location:" . $n); | |
} else { | |
$this->form(); | |
} | |
} | |
} | |
/** | |
* 登录文件管理验证 | |
*/ | |
public function form() | |
{ | |
$csrf_token=csrf_token(); | |
echo <<<EOF | |
<form action="" method="post" enctype="multipart/form-data"> | |
请输入密码 | |
<input type="hidden" name="_token" value="$csrf_token"> | |
<input type="password" name="password"> | |
<input type="submit" value="登录"> | |
</form> | |
EOF; | |
} | |
/** | |
* 头部 | |
* 引入必要的js,css | |
*/ | |
public function head() | |
{ | |
echo <<<EOF | |
<!DOCTYPE html> | |
<html lang="en"> | |
<!--begin code mirror --> | |
<!--下面两个是使用Code Mirror必须引入的--> | |
<link rel="stylesheet" href="/src/codemirror-5.31.0/lib/codemirror.css" /> | |
<script src="/src/codemirror-5.31.0/lib/codemirror.js"></script> | |
<!--Java代码高亮必须引入--> | |
<script src="/src/codemirror-5.31.0/clike.js"></script> | |
<!--groovy代码高亮--> | |
<script src="/src/codemirror-5.31.0/mode/groovy/groovy.js"></script> | |
<!--引入css文件,用以支持主题--> | |
<link rel="stylesheet" href="/src/codemirror-5.31.0/theme/dracula.css" /> | |
<!--支持代码折叠--> | |
<link rel="stylesheet" href="/src/codemirror-5.31.0/addon/fold/foldgutter.css" /> | |
<script src="/src/codemirror-5.31.0/addon/fold/foldcode.js"></script> | |
<script src="/src/codemirror-5.31.0/addon/fold/foldgutter.js"></script> | |
<script src="/src/codemirror-5.31.0/addon/fold/brace-fold.js"></script> | |
<script src="/src/codemirror-5.31.0/addon/fold/comment-fold.js"></script> | |
<!--括号匹配--> | |
<script src="/src/codemirror-5.31.0/addon/edit/matchbrackets.js"></script> | |
<!--end Code Mirror --> | |
<head> | |
<meta charset="utf-8" /> | |
<title>文件管理</title> | |
</head> | |
EOF; | |
} | |
/** | |
* 脚部 | |
* js 配置:代码折叠 | 设置主题 | 代码高亮| 设置代码框的长宽 | |
*/ | |
public function foot() | |
{ | |
echo <<<EOF | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("ed"), { | |
mode: "text/groovy", //实现groovy代码高亮 | |
mode: "text/x-java", //实现Java代码高亮 | |
lineNumbers: true, //显示行号 | |
theme: "dracula", //设置主题 | |
lineWrapping: true, //代码折叠 | |
foldGutter: true, | |
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], | |
matchBrackets: true, //括号匹配 | |
//readOnly: true, //只读 | |
}); | |
editor.setSize('100%', '400px'); //设置代码框的长宽 | |
</script> | |
</body> | |
</html> | |
EOF; | |
} | |
} |
code-management/src/CodeViewProvider.php
namespace Wqb\CodeView; | |
use Illuminate\Support\ServiceProvider; | |
class CodeViewProvider extends ServiceProvider | |
{ | |
/** | |
* Bootstrap the application services. | |
* | |
* @return void | |
*/ | |
public function boot() | |
{ | |
$this->loadRoutesFrom(__DIR__.'/../routes/codeview.php'); | |
$this->publishes([ | |
__DIR__.'/../config/codemirror-5.31.0' => base_path('public/src/codemirror-5.31.0'), | |
]); | |
} | |
/** | |
* Register the application services. | |
* | |
* @return void | |
*/ | |
public function register() | |
{ | |
$this->app->singleton('codeview', function ($app) { | |
return new CodeView($app['config']); | |
}); | |
} | |
} |
5、访问项目地址/codeview
登录页
列表页
查看编辑页
文件上传,新建目录
6、提交推送到git仓库
不懂可以百度,如何提交代码到github仓库
7、发布到packagist
- 注册登录Packagist(https://packagist.org/)
- 点击右上角Submit
- 将你的Github地址贴进去,点Check
8、可能遇到的问题
1、Could not find a version of package matching your minimum-stability (stable).
git tag v1.0 | |
git push origin v1.0 |
2、Could not find package
// 不要使用国内镜像,设置回 https://packagist.org | |
// 然后别忘了打开你的梯子 | |
composer config -g repo.packagist composer https://packagist.org |