create-prisma-php-app 1.13.3 → 1.13.500

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -403,6 +403,61 @@ function modifyOutputLayoutForError($contentToAdd)
403
403
  }
404
404
  }
405
405
 
406
+ function wireCallback($content)
407
+ {
408
+ $headers = getallheaders();
409
+ $wireRequest = isset($headers['http_pphp_wire_request']) ? strtolower($headers['http_pphp_wire_request']) : '';
410
+ $wireJsonRequest = isset($headers['http_pphp_wire_request_json']) ? strtolower($headers['http_pphp_wire_request_json']) : '';
411
+
412
+ if ($wireRequest === 'true') {
413
+ // Read input data
414
+ $input = file_get_contents('php://input');
415
+ $data = json_decode($input, true);
416
+
417
+ // Initialize response
418
+ $response = [
419
+ 'success' => false,
420
+ 'error' => 'Callback not provided',
421
+ 'data' => $data
422
+ ];
423
+
424
+ // Validate and call the dynamic function
425
+ if (isset($data['callback'])) {
426
+ // Sanitize and create a dynamic function name
427
+ $callbackName = preg_replace('/[^a-zA-Z0-9_]/', '', $data['callback']); // Sanitize
428
+
429
+ // Check if the dynamic function is defined and callable
430
+ if (function_exists($callbackName) && is_callable($callbackName)) {
431
+ $dataObject = new \ArrayObject($data, \ArrayObject::ARRAY_AS_PROPS);
432
+
433
+ // Call the anonymous function dynamically
434
+ $callbackResponse = call_user_func($callbackName, $dataObject);
435
+
436
+ // Prepare success response
437
+ $response = [
438
+ 'success' => true,
439
+ 'response' => $callbackResponse
440
+ ];
441
+ } else {
442
+ // Invalid callback provided
443
+ $response['error'] = 'Invalid callback';
444
+ }
445
+ }
446
+
447
+ if (!empty($response['response'])) {
448
+ if ($wireJsonRequest === 'true') {
449
+ header('Content-Type: application/json');
450
+ echo json_encode($response);
451
+ exit;
452
+ }
453
+ echo json_encode($response);
454
+ }
455
+
456
+ echo $content;
457
+ exit;
458
+ }
459
+ }
460
+
406
461
  try {
407
462
  $_determineContentToInclude = determineContentToInclude();
408
463
  checkForDuplicateRoutes();
@@ -460,6 +515,7 @@ try {
460
515
 
461
516
  if (!$_isContentIncluded && !$_isChildContentIncluded) {
462
517
  $content .= $childContent;
518
+ wireCallback($content);
463
519
  ob_start();
464
520
  require_once APP_PATH . '/layout.php';
465
521
  } else {
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let i=`http://localhost/${n}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e){const s=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(s,'const { createProxyMiddleware } = require("http-proxy-middleware");\nconst fs = require("fs");\n\nconst jsonData = fs.readFileSync("prisma-php.json", "utf8");\nconst config = JSON.parse(jsonData);\n\nmodule.exports = {\n proxy: "http://localhost:3000",\n middleware: [\n (req, res, next) => {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n createProxyMiddleware({\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n};',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8")),i=configureBrowserSyncCommand(e);n.scripts=Object.assign(Object.assign({},n.scripts),{projectName:"node settings/project-name.cjs"});let c=[];s.tailwindcss&&(n.scripts=Object.assign(Object.assign({},n.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),c.push("tailwind")),s.websocket&&(n.scripts=Object.assign(Object.assign({},n.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),c.push("websocket")),s.docker&&(n.scripts=Object.assign(Object.assign({},n.scripts),{docker:"docker-compose up"}),c.push("docker"));const r=Object.assign({},n.scripts);r["browser-sync"]=i,r.dev=c.length>0?`npm-run-all --parallel projectName browser-sync ${c.join(" ")}`:"npm-run-all --parallel projectName browser-sync",n.scripts=r,n.type="module",s.prisma&&(n.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(n.require=Object.assign(Object.assign({},n.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s,t){var n;const i=fs.existsSync(e),c=i&&fs.statSync(e);if(i&&c&&c.isDirectory()){const i=s.toLowerCase();if(!t.websocket&&i.includes("src\\lib\\websocket"))return;if(!t.prisma&&i.includes("src\\lib\\prisma"))return;const c=s.replace(/\\/g,"/");if(null===(n=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===n?void 0:n.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.cjs")||s.includes("restart-websocket.bat")))return;if(!t.htmx&&s.includes("htmx.min.js"))return;if(!t.docker&&(s.includes(".dockerignore")||s.includes("docker-compose.yml")||s.includes("Dockerfile")||s.includes("apache.conf")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({srcDir:s,destDir:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\n },\n};",{flag:"w"})}function modifyLayoutPHP(e,s){const t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">',i=s.tailwindcss?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`,c=s.htmx?`${i}\n <script src="<?php echo $baseUrl; ?>js/htmx.min.js"><\/script>`:`${i}`;e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(t.push({src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));const n=[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/src",destDir:"/src"}];s.prisma&&n.push({srcDir:"/prisma",destDir:"/prisma"}),s.docker&&n.push({srcDir:"/.dockerignore",destDir:"/.dockerignore"},{srcDir:"/docker-compose.yml",destDir:"/docker-compose.yml"},{srcDir:"/Dockerfile",destDir:"/Dockerfile"},{srcDir:"/apache.conf",destDir:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,s),modifyPostcssConfig(e)):modifyLayoutPHP(e,s);const i='# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \n \n# PHPMailer\n# SMTP_HOST=smtp.gmail.com or your SMTP host\n# SMTP_USERNAME=john.doe@gmail.com or your SMTP username\n# SMTP_PASSWORD=123456\n# SMTP_PORT=587 for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION=ssl or tls\n# MAIL_FROM=john.doe@gmail.com\n# MAIL_FROM_NAME="John Doe"';if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${i}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,i);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){var s,t,n,i,c,r;const o=[];e.projectName||o.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||o.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||o.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||o.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.htmx||o.push({type:"toggle",name:"htmx",message:`Would you like to use ${chalk.blue("htmx")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||o.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"});const a=o;if(0===a.length&&e.projectName)return e;const l=()=>{process.exit(0)};try{const o=await prompts(a,{onCancel:l});return 0===Object.keys(o).length?null:{projectName:o.projectName?String(o.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=o.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=o.websocket)&&void 0!==n?n:e.websocket,prisma:null!==(i=o.prisma)&&void 0!==i?i:e.prisma,htmx:null!==(c=o.htmx)&&void 0!==c?c:e.htmx,docker:null!==(r=o.docker)&&void 0!==r?r:e.docker}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};async function main(){var e,s,t,n,i,c,r,o;try{const a=process.argv.slice(2);let l=a[0],p=null;if(l){const o={projectName:l,tailwindcss:a.includes("--tailwindcss"),websocket:a.includes("--websocket"),prisma:a.includes("--prisma"),htmx:a.includes("--htmx"),docker:a.includes("--docker")};if(p=await getAnswer(o),null===p)return;const d=process.cwd(),u=path.join(d,"prisma-php.json"),h=readJsonFile(u);let m=[];null===(e=h.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(d,e);fs.existsSync(s)&&m.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:l,tailwindcss:null!==(s=null==p?void 0:p.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==p?void 0:p.websocket)&&void 0!==t&&t,prisma:null!==(n=null==p?void 0:p.prisma)&&void 0!==n&&n,htmx:null!==(i=null==p?void 0:p.htmx)&&void 0!==i&&i,docker:null!==(c=null==p?void 0:p.docker)&&void 0!==c&&c,isUpdate:!0,excludeFiles:null!==(r=h.excludeFiles)&&void 0!==r?r:[],excludeFilePath:null!=m?m:[],filePath:d}}else p=await getAnswer();if(null===p)return;execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm uninstall -g browser-sync",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"}),l||fs.mkdirSync(p.projectName);const d=process.cwd();let u=l?d:path.join(d,p.projectName);l||process.chdir(p.projectName);const h=["typescript","@types/node","ts-node","http-proxy-middleware@^3.0.0","npm-run-all"];p.tailwindcss&&h.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),p.websocket&&h.push("chokidar-cli"),p.prisma&&h.push("prisma","@prisma/client"),await installDependencies(u,h,!0),l||execSync("npx tsc --init",{stdio:"inherit"}),p.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),p.prisma&&(fs.existsSync(path.join(u,"prisma"))||execSync("npx prisma init",{stdio:"inherit"})),await createDirectoryStructure(u,p);const m=path.join(u,"public");if(fs.existsSync(m)||fs.mkdirSync(m),null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(u,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}if(updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),!updateAnswer.docker){[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"].forEach((e=>{const s=path.join(u,e);fs.existsSync(s)&&fs.unlinkSync(s)}))}e.length>0&&await uninstallDependencies(u,e,!0)}const f=await fetchPackageVersion("create-prisma-php-app"),w=u.replace(/\\/g,"\\"),y=bsConfigUrls(w),g=p.prisma?"src/Lib/Prisma/Classes":"",j={projectName:p.projectName,projectRootPath:w,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",phpGenerateClassPath:g,bsTarget:y.bsTarget,bsPathRewrite:y.bsPathRewrite,tailwindcss:p.tailwindcss,websocket:p.websocket,prisma:p.prisma,htmx:p.htmx,docker:p.docker,version:f,excludeFiles:null!==(o=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==o?o:[]};fs.writeFileSync(path.join(u,"prisma-php.json"),JSON.stringify(j,null,2),{flag:"w"}),(null==updateAnswer?void 0:updateAnswer.isUpdate)?execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",{stdio:"inherit"}):execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"})}catch(e){process.exit(1)}}main();
2
+ import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let i=`http://localhost/${n}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e){const s=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(s,'const { createProxyMiddleware } = require("http-proxy-middleware");\nconst fs = require("fs");\n\nconst jsonData = fs.readFileSync("prisma-php.json", "utf8");\nconst config = JSON.parse(jsonData);\n\nmodule.exports = {\n proxy: "http://localhost:3000",\n middleware: [\n (req, res, next) => {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n createProxyMiddleware({\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n};',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8")),i=configureBrowserSyncCommand(e);n.scripts=Object.assign(Object.assign({},n.scripts),{projectName:"node settings/project-name.cjs"});let c=[];s.tailwindcss&&(n.scripts=Object.assign(Object.assign({},n.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),c.push("tailwind")),s.websocket&&(n.scripts=Object.assign(Object.assign({},n.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),c.push("websocket")),s.docker&&(n.scripts=Object.assign(Object.assign({},n.scripts),{docker:"docker-compose up"}),c.push("docker"));const r=Object.assign({},n.scripts);r["browser-sync"]=i,r.dev=c.length>0?`npm-run-all --parallel projectName browser-sync ${c.join(" ")}`:"npm-run-all --parallel projectName browser-sync",n.scripts=r,n.type="module",s.prisma&&(n.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(n.require=Object.assign(Object.assign({},n.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s,t){var n;const i=fs.existsSync(e),c=i&&fs.statSync(e);if(i&&c&&c.isDirectory()){const i=s.toLowerCase();if(!t.websocket&&i.includes("src\\lib\\websocket"))return;if(!t.prisma&&i.includes("src\\lib\\prisma"))return;const c=s.replace(/\\/g,"/");if(null===(n=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===n?void 0:n.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.cjs")||s.includes("restart-websocket.bat")))return;if(!t.docker&&(s.includes(".dockerignore")||s.includes("docker-compose.yml")||s.includes("Dockerfile")||s.includes("apache.conf")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({srcDir:s,destDir:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\n },\n};",{flag:"w"})}function modifyLayoutPHP(e,s){const t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">',i=s.tailwindcss?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(t.push({src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));const n=[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/src",destDir:"/src"}];s.prisma&&n.push({srcDir:"/prisma",destDir:"/prisma"}),s.docker&&n.push({srcDir:"/.dockerignore",destDir:"/.dockerignore"},{srcDir:"/docker-compose.yml",destDir:"/docker-compose.yml"},{srcDir:"/Dockerfile",destDir:"/Dockerfile"},{srcDir:"/apache.conf",destDir:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,s),modifyPostcssConfig(e)):modifyLayoutPHP(e,s);const i='# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \n \n# PHPMailer\n# SMTP_HOST=smtp.gmail.com or your SMTP host\n# SMTP_USERNAME=john.doe@gmail.com or your SMTP username\n# SMTP_PASSWORD=123456\n# SMTP_PORT=587 for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION=ssl or tls\n# MAIL_FROM=john.doe@gmail.com\n# MAIL_FROM_NAME="John Doe"';if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${i}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,i);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){var s,t,n,i,c;const r=[];e.projectName||r.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||r.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||r.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||r.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||r.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"});const o=r;if(0===o.length&&e.projectName)return e;const a=()=>{process.exit(0)};try{const r=await prompts(o,{onCancel:a});return 0===Object.keys(r).length?null:{projectName:r.projectName?String(r.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=r.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=r.websocket)&&void 0!==n?n:e.websocket,prisma:null!==(i=r.prisma)&&void 0!==i?i:e.prisma,docker:null!==(c=r.docker)&&void 0!==c?c:e.docker}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}async function main(){var e,s,t,n,i,c,r;try{const o=process.argv.slice(2);let a=o[0],l=null;if(a){const r={projectName:a,tailwindcss:o.includes("--tailwindcss"),websocket:o.includes("--websocket"),prisma:o.includes("--prisma"),docker:o.includes("--docker")};if(l=await getAnswer(r),null===l)return;const p=process.cwd(),d=path.join(p,"prisma-php.json"),u=readJsonFile(d);let h=[];null===(e=u.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(p,e);fs.existsSync(s)&&h.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:a,tailwindcss:null!==(s=null==l?void 0:l.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==l?void 0:l.websocket)&&void 0!==t&&t,prisma:null!==(n=null==l?void 0:l.prisma)&&void 0!==n&&n,docker:null!==(i=null==l?void 0:l.docker)&&void 0!==i&&i,isUpdate:!0,excludeFiles:null!==(c=u.excludeFiles)&&void 0!==c?c:[],excludeFilePath:null!=h?h:[],filePath:p}}else l=await getAnswer();if(null===l)return;const p=await fetchPackageVersion("create-prisma-php-app"),d=getInstalledPackageVersion("create-prisma-php-app");d?-1===compareVersions(d,p)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const u=await fetchPackageVersion("browser-sync"),h=getInstalledPackageVersion("browser-sync");h?-1===compareVersions(h,u)&&(execSync("npm uninstall -g browser-sync",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"})):execSync("npm install -g browser-sync",{stdio:"inherit"}),a||fs.mkdirSync(l.projectName);const m=process.cwd();let f=a?m:path.join(m,l.projectName);a||process.chdir(l.projectName);const w=["typescript","@types/node","ts-node","http-proxy-middleware@^3.0.0","npm-run-all"];l.tailwindcss&&w.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),l.websocket&&w.push("chokidar-cli"),l.prisma&&w.push("prisma","@prisma/client"),await installDependencies(f,w,!0),a||execSync("npx tsc --init",{stdio:"inherit"}),l.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),l.prisma&&(fs.existsSync(path.join(f,"prisma"))||execSync("npx prisma init",{stdio:"inherit"})),await createDirectoryStructure(f,l);const g=path.join(f,"public");if(fs.existsSync(g)||fs.mkdirSync(g),null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(f,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}if(updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),!updateAnswer.docker){[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"].forEach((e=>{const s=path.join(f,e);fs.existsSync(s)&&fs.unlinkSync(s)}))}e.length>0&&await uninstallDependencies(f,e,!0)}const y=f.replace(/\\/g,"\\"),S=bsConfigUrls(y),k=l.prisma?"src/Lib/Prisma/Classes":"",j={projectName:l.projectName,projectRootPath:y,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",phpGenerateClassPath:k,bsTarget:S.bsTarget,bsPathRewrite:S.bsPathRewrite,tailwindcss:l.tailwindcss,websocket:l.websocket,prisma:l.prisma,docker:l.docker,version:p,excludeFiles:null!==(r=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==r?r:[]};fs.writeFileSync(path.join(f,"prisma-php.json"),JSON.stringify(j,null,2),{flag:"w"}),(null==updateAnswer?void 0:updateAnswer.isUpdate)?execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",{stdio:"inherit"}):execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"})}catch(e){process.exit(1)}}main();
@@ -35,7 +35,6 @@ const main = async () => {
35
35
  if (localSettings.tailwindcss) commandArgs.push("--tailwindcss");
36
36
  if (localSettings.websocket) commandArgs.push("--websocket");
37
37
  if (localSettings.prisma) commandArgs.push("--prisma");
38
- if (localSettings.htmx) commandArgs.push("--htmx");
39
38
  if (localSettings.docker) commandArgs.push("--docker");
40
39
  console.log("Executing command...\n");
41
40
  await executeCommand("npx", [
@@ -1,8 +1,8 @@
1
1
  <?php
2
2
 
3
- function redirect(string $url): void
3
+ function redirect(string $url, bool $replace = true, int $response_code = 0): void
4
4
  {
5
- header("Location: $url");
5
+ header("Location: $url", $replace, $response_code);
6
6
  exit;
7
7
  }
8
8
 
@@ -15,11 +15,6 @@ function isAjaxRequest()
15
15
  $isAjax = true;
16
16
  }
17
17
 
18
- // Check for HTMX request header
19
- if (!empty($_SERVER['HTTP_HX_REQUEST'])) {
20
- $isAjax = true;
21
- }
22
-
23
18
  // Check for common AJAX content types
24
19
  if (!empty($_SERVER['CONTENT_TYPE'])) {
25
20
  $ajaxContentTypes = [
@@ -23,7 +23,6 @@ $isPatch = $requestMethod === 'PATCH';
23
23
  $isHead = $requestMethod === 'HEAD';
24
24
  $isOptions = $requestMethod === 'OPTIONS';
25
25
  $isAjax = isAjaxRequest();
26
- $isHtmx = !empty($_SERVER['HTTP_HX_REQUEST']) && strtolower($_SERVER['HTTP_HX_REQUEST']) === 'true';
27
26
  $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
28
27
  $requestedWith = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? '';
29
28
  $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
@@ -595,15 +595,10 @@ class FormHandler
595
595
  }
596
596
  }
597
597
 
598
- let formHandler = null;
598
+ let formHandler = FormHandler ? new FormHandler() : null;
599
599
  // Initialize FormHandler on initial page load
600
600
  document.addEventListener('DOMContentLoaded', function() {
601
601
  formHandler = new FormHandler();
602
602
  });
603
-
604
- // Reinitialize FormHandler when HTMX swaps content
605
- document.body.addEventListener('htmx:afterOnLoad', function() {
606
- formHandler = new FormHandler();
607
- });
608
603
  }
609
604
  </script>
@@ -15,12 +15,17 @@ class StateManager
15
15
  * Constructs a new StateManager instance.
16
16
  *
17
17
  * @param array $initialState The initial state of the application.
18
+ * @param string|array|null $keepState The keys of the state to retain if resetting.
18
19
  */
19
- public function __construct($initialState = [])
20
+ public function __construct($initialState = [], $keepState = null)
20
21
  {
22
+ global $isAjax;
23
+
21
24
  $this->state = $initialState;
22
25
  $this->listeners = [];
23
26
  $this->loadState();
27
+
28
+ if (!$isAjax) $this->resetState($keepState);
24
29
  }
25
30
 
26
31
  /**
@@ -32,27 +37,30 @@ class StateManager
32
37
  public function getState($key = null)
33
38
  {
34
39
  if ($key === null) {
35
- return $this->state;
40
+ return new \ArrayObject($this->state, \ArrayObject::ARRAY_AS_PROPS);
41
+ }
42
+
43
+ if (array_key_exists($key, $this->state)) {
44
+ $value = $this->state[$key];
45
+ return is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
36
46
  }
37
47
 
38
- return $this->state[$key] ?? null;
48
+ return null;
39
49
  }
40
50
 
41
51
  /**
42
52
  * Updates the application state with the given update.
43
53
  *
44
54
  * @param array $update The state update to apply.
45
- * @param bool $saveToStorage Whether to save the updated state to storage.
46
55
  */
47
- public function setState($update, $saveToStorage = false)
56
+ public function setState($update)
48
57
  {
49
58
  $this->state = array_merge($this->state, $update);
50
59
  foreach ($this->listeners as $listener) {
51
60
  call_user_func($listener, $this->state);
52
61
  }
53
- if ($saveToStorage) {
54
- $this->saveState();
55
- }
62
+
63
+ $this->saveState();
56
64
  }
57
65
 
58
66
  /**
@@ -96,23 +104,30 @@ class StateManager
96
104
  /**
97
105
  * Resets the application state partially or completely.
98
106
  *
99
- * @param string|array|null $key The key(s) of the state to reset. If null, resets the entire state.
100
- * Can be a string for a single key or an array of strings for multiple keys.
101
- * @param bool $clearFromStorage Whether to clear the state from storage.
107
+ * @param string|array|null $keepKeys The key(s) of the state to retain. If null, resets the entire state.
108
+ * Can be a string for a single key or an array of strings for multiple keys.
102
109
  */
103
- public function resetState($key = null, $clearFromStorage = false)
110
+ public function resetState($keepKeys = null)
104
111
  {
105
- if ($key === null) {
112
+ if ($keepKeys === null) {
106
113
  // Reset the entire state
107
114
  $this->state = [];
108
- } elseif (is_array($key)) {
109
- // Reset only the parts of the state identified by the keys in the array
110
- foreach ($key as $k) {
111
- unset($this->state[$k]);
115
+ } elseif (is_array($keepKeys)) {
116
+ // Retain only the parts of the state identified by the keys in the array
117
+ $retainedState = [];
118
+ foreach ($keepKeys as $key) {
119
+ if (array_key_exists($key, $this->state)) {
120
+ $retainedState[$key] = $this->state[$key];
121
+ }
112
122
  }
123
+ $this->state = $retainedState;
113
124
  } else {
114
- // Reset only the part of the state identified by a single key
115
- unset($this->state[$key]);
125
+ // Retain only the part of the state identified by a single key
126
+ if (array_key_exists($keepKeys, $this->state)) {
127
+ $this->state = [$keepKeys => $this->state[$keepKeys]];
128
+ } else {
129
+ $this->state = [];
130
+ }
116
131
  }
117
132
 
118
133
  // Notify all listeners about the state change
@@ -120,13 +135,11 @@ class StateManager
120
135
  call_user_func($listener, $this->state);
121
136
  }
122
137
 
123
- if ($clearFromStorage) {
124
- // Save the updated state to the session or clear it
125
- if (empty($this->state)) {
126
- unset($_SESSION[self::APP_STATE]);
127
- } else {
128
- $_SESSION[self::APP_STATE] = json_encode($this->state);
129
- }
138
+ // Save the updated state to the session or clear it
139
+ if (empty($this->state)) {
140
+ unset($_SESSION[self::APP_STATE]);
141
+ } else {
142
+ $_SESSION[self::APP_STATE] = json_encode($this->state);
130
143
  }
131
144
  }
132
145
  }
@@ -28,6 +28,16 @@ class Validator
28
28
  return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
29
29
  }
30
30
 
31
+ public static function validateDate($value)
32
+ {
33
+ $date = \DateTime::createFromFormat('Y-m-d', $value);
34
+ if ($date && $date->format('Y-m-d') === $value) {
35
+ return $value;
36
+ } else {
37
+ return null;
38
+ }
39
+ }
40
+
31
41
  public static function validateDateTime($value)
32
42
  {
33
43
  $date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
@@ -44,49 +54,11 @@ class Validator
44
54
  return json_last_error() === JSON_ERROR_NONE;
45
55
  }
46
56
 
47
- public static function validateBigInt($value)
48
- {
49
- return filter_var($value, FILTER_VALIDATE_INT); // Or custom validation for larger numbers
50
- }
51
-
52
- public static function validateBytes($value)
53
- {
54
- // Custom validation based on your application's handling of binary data
55
- return true; // Placeholder
56
- }
57
-
58
- public static function validateDecimal($value)
59
- {
60
- return filter_var($value, FILTER_VALIDATE_FLOAT); // Or custom validation for fixed-point numbers
61
- }
62
-
63
57
  public static function validateEnum($value, $allowedValues)
64
58
  {
65
59
  return in_array($value, $allowedValues, true);
66
60
  }
67
61
 
68
- public static function validateUnique($value)
69
- {
70
- // This validation should be handled at the database level or in your application logic
71
- return true; // Placeholder
72
- }
73
-
74
- public static function validateId($value)
75
- {
76
- // Validate based on your application's ID format (e.g., integer, UUID)
77
- return static::validateInt($value); // Placeholder
78
- }
79
-
80
- public static function validateDate($value)
81
- {
82
- $date = \DateTime::createFromFormat('Y-m-d', $value);
83
- if ($date && $date->format('Y-m-d') === $value) {
84
- return $value;
85
- } else {
86
- return null;
87
- }
88
- }
89
-
90
62
  public static function validateEmail($value)
91
63
  {
92
64
  if ($value === null) {
@@ -104,10 +76,4 @@ class Validator
104
76
 
105
77
  return filter_var($value, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
106
78
  }
107
-
108
- public static function validateUnsupported($value)
109
- {
110
- // Placeholder for future data types
111
- return true; // No validation
112
- }
113
79
  }