create-prisma-php-app 2.0.0-alpha.9 → 2.0.0-beta.2

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.
package/dist/.htaccess CHANGED
@@ -1,11 +1,11 @@
1
1
  # Turn on rewrite engine
2
2
  RewriteEngine On
3
3
 
4
- # Deny access to .env file for security
5
- <Files .env>
4
+ # Prevent access to sensitive files
5
+ <FilesMatch "(^\.htaccess|\.git|\.env|composer\.(json|lock)|package(-lock)?\.json|phpunit\.xml)$">
6
6
  Order allow,deny
7
7
  Deny from all
8
- </Files>
8
+ </FilesMatch>
9
9
 
10
10
  # Allow cross-origin requests (CORS) for all routes
11
11
  <IfModule mod_headers.c>
@@ -32,11 +32,38 @@ RewriteEngine On
32
32
  </FilesMatch>
33
33
  </IfModule>
34
34
 
35
+ # Add important security headers
36
+ <IfModule mod_headers.c>
37
+ # Enforce HTTPS and prevent protocol downgrade attacks
38
+ Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
39
+
40
+ # Protect against Cross-Site Scripting (XSS) attacks
41
+ Header set X-XSS-Protection "1; mode=block"
42
+
43
+ # Prevent MIME-type sniffing
44
+ Header set X-Content-Type-Options "nosniff"
45
+
46
+ # Clickjacking protection
47
+ Header always set X-Frame-Options "DENY"
48
+
49
+ # Implement a basic Content Security Policy (CSP)
50
+ Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;"
51
+
52
+ # Restrict form submissions
53
+ Header set Content-Security-Policy "form-action 'self'"
54
+
55
+ # Set a strict Referrer Policy
56
+ Header set Referrer-Policy "strict-origin-when-cross-origin"
57
+
58
+ # Control browser permissions (optional but recommended)
59
+ Header set Permissions-Policy "geolocation=(), microphone=(), camera=(), autoplay=()"
60
+ </IfModule>
61
+
35
62
  # Exclude static files from being redirected
36
63
  RewriteCond %{REQUEST_URI} !\.(css|js|png|jpe?g|gif|svg|webp|woff2?|ttf|eot|ico|pdf|mp4|webm|mp3|ogg)$ [NC]
37
64
  RewriteCond %{REQUEST_URI} !^/bootstrap.php
38
65
  RewriteRule ^(.*)$ bootstrap.php [QSA,L]
39
66
 
40
- # Add this to ensure OPTIONS requests are handled correctly
67
+ # Ensure OPTIONS requests are handled correctly
41
68
  RewriteCond %{REQUEST_METHOD} OPTIONS
42
69
  RewriteRule ^ - [R=200,L]
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
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";import{randomBytes}from"crypto";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];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 c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const i=c.replace(/(?<!:)(\/\/+)/g,"/"),o=n.replace(/\/\/+/g,"/");return{bsTarget:`${i}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let i={...n.scripts};i.browserSync="tsx settings/bs-config.ts",i.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=i,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),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={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...n.require,"calicastle/cuid":"^2.0.0"}),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\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateLocalStoreKey(){return randomBytes(16).toString("hex")}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(!t.prisma&&n.includes("src\\lib\\prisma"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.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.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if(!t.prisma&&(s.includes("prisma-sdk.ts")||s.includes("prisma-schema.json")))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\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"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${n}`:n),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){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.prisma&&n.push({src:"/prisma",dest:"/prisma"}),s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const i=fs.readFileSync(n,"utf8");fs.writeFileSync(c,i,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=`# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET="${generateAuthSecret()}"\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"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS="true"\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV="development"\n\n# APP CACHE ENABLED - Set to true to enable caching - Default is false\nCACHE_ENABLED="false"\n# APP CACHE TTL - Set the cache time to live in seconds - Default is 600 seconds (10 minutes)\nCACHE_TTL="600"\n\n# LOCAL STORAGE KEY - Define a custom key for local storage.\n# If not set, it defaults to "pphp_local_store_59e13".\n# Spaces in the value will be replaced with underscores, and the key will be converted to lowercase automatically.\nLOCALSTORE_KEY="${generateLocalStoreKey()}"`;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${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c)}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}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}}
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";import{randomBytes}from"crypto";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];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 c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const o=c.replace(/(?<!:)(\/\/+)/g,"/"),i=n.replace(/\/\/+/g,"/");return{bsTarget:`${o}/`,bsPathRewrite:{"^/":`/${i.startsWith("/")?i.substring(1):i}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let o={...n.scripts};o.browserSync="tsx settings/bs-config.ts",o.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=o,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),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={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...n.require,"calicastle/cuid":"^2.0.0"}),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\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateLocalStoreKey(){return randomBytes(16).toString("hex")}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.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.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\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"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${n}`:n),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){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const o=fs.readFileSync(n,"utf8");fs.writeFileSync(c,o,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=`# Prisma PHP Auth Secret Key\nAUTH_SECRET="${generateAuthSecret()}"\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"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS="true"\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV="development"\n\n# APP CACHE ENABLED - Set to true to enable caching - Default is false\nCACHE_ENABLED="false"\n# APP CACHE TTL - Set the cache time to live in seconds - Default is 600 seconds (10 minutes)\nCACHE_TTL="600"\n\n# LOCAL STORAGE KEY - Define a custom key for local storage.\n# If not set, it defaults to "pphp_local_store_59e13".\n# Spaces in the value will be replaced with underscores, and the key will be converted to lowercase automatically.\nLOCALSTORE_KEY="${generateLocalStoreKey()}"`;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${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c)}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const o=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:o.swaggerDocs??e.swaggerDocs??!1,tailwindcss:o.tailwindcss??e.tailwindcss??!1,websocket:o.websocket??e.websocket??!1,prisma:o.prisma??e.prisma??!1,docker:o.docker??e.docker??!1}}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}}
3
3
  /**
4
4
  * Install dependencies in the specified directory.
5
5
  * @param {string} baseDir - The base directory where to install the dependencies.
@@ -32,26 +32,24 @@ async function installDependencies(baseDir, dependencies, isDev = false) {
32
32
  });
33
33
  }
34
34
  const pinnedVersions = {
35
- "@prisma/client": "^6.4.0",
36
- "@prisma/internals": "^6.4.0",
37
- "@tailwindcss/postcss": "^4.0.7",
35
+ "@tailwindcss/postcss": "^4.0.9",
38
36
  "@types/browser-sync": "^2.29.0",
39
- "@types/node": "^22.13.4",
37
+ "@types/node": "^22.13.8",
40
38
  "@types/prompts": "^2.4.9",
41
39
  autoprefixer: "^10.4.20",
42
40
  "browser-sync": "^3.0.3",
43
41
  chalk: "^5.4.1",
44
42
  "chokidar-cli": "^3.0.0",
43
+ cssnano: "^7.0.6",
45
44
  "http-proxy-middleware": "^3.0.3",
46
45
  "npm-run-all": "^4.1.5",
47
46
  "php-parser": "^3.2.2",
48
- postcss: "^8.5.2",
47
+ postcss: "^8.5.3",
49
48
  "postcss-cli": "^11.0.0",
50
- prisma: "^6.4.0",
51
49
  prompts: "^2.4.2",
52
- tailwindcss: "^4.0.7",
50
+ tailwindcss: "^4.0.9",
53
51
  tsx: "^4.19.3",
54
- typescript: "^5.7.3",
52
+ typescript: "^5.8.2",
55
53
  };
56
54
  function pkg(name) {
57
55
  return pinnedVersions[name] ? `${name}@${pinnedVersions[name]}` : name;
@@ -84,26 +82,28 @@ async function main() {
84
82
  }
85
83
  const currentDir = process.cwd();
86
84
  const configPath = path.join(currentDir, "prisma-php.json");
87
- const localSettings = readJsonFile(configPath);
88
- let excludeFiles = [];
89
- localSettings.excludeFiles?.map((file) => {
90
- const filePath = path.join(currentDir, file);
91
- if (fs.existsSync(filePath))
92
- excludeFiles.push(filePath.replace(/\\/g, "/"));
93
- });
94
- updateAnswer = {
95
- projectName,
96
- backendOnly: answer?.backendOnly ?? false,
97
- swaggerDocs: answer?.swaggerDocs ?? false,
98
- tailwindcss: answer?.tailwindcss ?? false,
99
- websocket: answer?.websocket ?? false,
100
- prisma: answer?.prisma ?? false,
101
- docker: answer?.docker ?? false,
102
- isUpdate: true,
103
- excludeFiles: localSettings.excludeFiles ?? [],
104
- excludeFilePath: excludeFiles ?? [],
105
- filePath: currentDir,
106
- };
85
+ if (fs.existsSync(configPath)) {
86
+ const localSettings = readJsonFile(configPath);
87
+ let excludeFiles = [];
88
+ localSettings.excludeFiles?.map((file) => {
89
+ const filePath = path.join(currentDir, file);
90
+ if (fs.existsSync(filePath))
91
+ excludeFiles.push(filePath.replace(/\\/g, "/"));
92
+ });
93
+ updateAnswer = {
94
+ projectName,
95
+ backendOnly: answer?.backendOnly ?? false,
96
+ swaggerDocs: answer?.swaggerDocs ?? false,
97
+ tailwindcss: answer?.tailwindcss ?? false,
98
+ websocket: answer?.websocket ?? false,
99
+ prisma: answer?.prisma ?? false,
100
+ docker: answer?.docker ?? false,
101
+ isUpdate: true,
102
+ excludeFiles: localSettings.excludeFiles ?? [],
103
+ excludeFilePath: excludeFiles ?? [],
104
+ filePath: currentDir,
105
+ };
106
+ }
107
107
  } else {
108
108
  answer = await getAnswer();
109
109
  }
@@ -124,10 +124,10 @@ async function main() {
124
124
  latestVersionOfCreatePrismaPhpApp
125
125
  ) === -1
126
126
  ) {
127
- execSync(`npm uninstall -g create-prisma-php-app`, {
127
+ execSync("npm uninstall -g create-prisma-php-app", {
128
128
  stdio: "inherit",
129
129
  });
130
- execSync(`npm install -g create-prisma-php-app`, {
130
+ execSync("npm install -g create-prisma-php-app", {
131
131
  stdio: "inherit",
132
132
  });
133
133
  }
@@ -156,40 +156,31 @@ async function main() {
156
156
  dependencies.push(pkg("swagger-jsdoc"), pkg("@types/swagger-jsdoc"));
157
157
  }
158
158
  if (answer.swaggerDocs && answer.prisma) {
159
- dependencies.push(
160
- pkg("prompts"),
161
- pkg("@types/prompts"),
162
- pkg("@prisma/internals")
163
- );
159
+ dependencies.push(pkg("prompts"), pkg("@types/prompts"));
164
160
  }
165
161
  if (answer.tailwindcss) {
166
162
  dependencies.push(
167
163
  pkg("tailwindcss"),
168
164
  pkg("postcss"),
169
165
  pkg("postcss-cli"),
170
- pkg("@tailwindcss/postcss")
166
+ pkg("@tailwindcss/postcss"),
167
+ pkg("cssnano")
171
168
  );
172
169
  }
173
170
  if (answer.websocket) {
174
171
  dependencies.push(pkg("chokidar-cli"));
175
172
  }
176
173
  if (answer.prisma) {
177
- dependencies.push(
178
- pkg("prisma"),
179
- pkg("@prisma/client"),
180
- pkg("@prisma/internals")
181
- );
182
174
  execSync("npm install -g prisma-client-php", { stdio: "inherit" });
183
175
  }
184
176
  await installDependencies(projectPath, dependencies, true);
185
177
  if (!projectName) {
186
- execSync(`npx tsc --init`, { stdio: "inherit" });
178
+ execSync("npx tsc --init", { stdio: "inherit" });
187
179
  }
180
+ await createDirectoryStructure(projectPath, answer);
188
181
  if (answer.prisma) {
189
- if (!fs.existsSync(path.join(projectPath, "prisma")))
190
- execSync(`npx prisma init`, { stdio: "inherit" });
182
+ execSync("npx ppo init --prisma-php", { stdio: "inherit" });
191
183
  }
192
- await createDirectoryStructure(projectPath, answer);
193
184
  if (answer.swaggerDocs) {
194
185
  const swaggerDocsPath = path.join(
195
186
  projectPath,
@@ -264,8 +255,7 @@ async function main() {
264
255
  "swagger-jsdoc",
265
256
  "@types/swagger-jsdoc",
266
257
  "prompts",
267
- "@types/prompts",
268
- "@prisma/internals"
258
+ "@types/prompts"
269
259
  );
270
260
  }
271
261
  if (!updateAnswer.tailwindcss) {
@@ -283,7 +273,8 @@ async function main() {
283
273
  "tailwindcss",
284
274
  "postcss",
285
275
  "postcss-cli",
286
- "@tailwindcss/postcss"
276
+ "@tailwindcss/postcss",
277
+ "cssnano"
287
278
  );
288
279
  }
289
280
  if (!updateAnswer.websocket) {
@@ -1,5 +1,6 @@
1
1
  export default {
2
2
  plugins: {
3
3
  "@tailwindcss/postcss": {},
4
+ cssnano: {},
4
5
  },
5
6
  };
@@ -16,7 +16,7 @@ class ChatGPTClient
16
16
  private string $apiKey = '';
17
17
  private array $cache = [];
18
18
 
19
- public function __construct(Client $client = null)
19
+ public function __construct(?Client $client = null)
20
20
  {
21
21
  // Initialize the Guzzle HTTP client, allowing for dependency injection
22
22
  $this->client = $client ?: new Client();
@@ -74,7 +74,7 @@ class Auth
74
74
  * echo "Error: " . $e->getMessage();
75
75
  * }
76
76
  */
77
- public function signIn($data, string $tokenValidity = null): string
77
+ public function signIn($data, ?string $tokenValidity = null): string
78
78
  {
79
79
  if (!$this->secretKey) {
80
80
  throw new \InvalidArgumentException("Secret key is required for authentication.");
@@ -210,7 +210,7 @@ class Auth
210
210
  *
211
211
  * @throws InvalidArgumentException Thrown if the token is invalid.
212
212
  */
213
- public function refreshToken(string $jwt, string $tokenValidity = null): string
213
+ public function refreshToken(string $jwt, ?string $tokenValidity = null): string
214
214
  {
215
215
  $decodedToken = $this->verifyToken($jwt);
216
216
 
@@ -256,7 +256,7 @@ class Auth
256
256
  *
257
257
  * @return void
258
258
  */
259
- public function signOut(string $redirect = null)
259
+ public function signOut(?string $redirect = null)
260
260
  {
261
261
  if (isset($_COOKIE[self::COOKIE_NAME])) {
262
262
  unset($_COOKIE[self::COOKIE_NAME]);
@@ -4,6 +4,8 @@ declare(strict_types=1);
4
4
 
5
5
  namespace Lib\Auth;
6
6
 
7
+ use ArrayObject;
8
+
7
9
  enum AuthRole: string
8
10
  {
9
11
  case Admin = 'Admin';
@@ -67,13 +69,13 @@ final class AuthConfig
67
69
  /**
68
70
  * Checks if the given user role is authorized to access a set of roles.
69
71
  *
70
- * @param \ArrayObject|string $userRole The user's role to check.
72
+ * @param ArrayObject|string $userRole The user's role to check.
71
73
  * @param array<AuthRole> $roles An array of AuthRole instances specifying allowed roles.
72
74
  * @return bool Returns true if the user's role matches any of the allowed roles, false otherwise.
73
75
  */
74
- public static function checkAuthRole(\ArrayObject|string $userRole, array $roles): bool
76
+ public static function checkAuthRole(ArrayObject|string $userRole, array $roles): bool
75
77
  {
76
- if ($userRole instanceof \ArrayObject) {
78
+ if ($userRole instanceof ArrayObject) {
77
79
  $userRole = $userRole[Auth::ROLE_NAME] ?? '';
78
80
  }
79
81
 
@@ -5,6 +5,9 @@ declare(strict_types=1);
5
5
  namespace Lib;
6
6
 
7
7
  use Lib\Headers\Boom;
8
+ use ArrayObject;
9
+ use stdClass;
10
+ use Lib\PrismaPHPSettings;
8
11
 
9
12
  class Request
10
13
  {
@@ -16,7 +19,7 @@ class Request
16
19
  public const baseUrl = '/src/app';
17
20
 
18
21
  /**
19
- * @var \stdClass $params A static property to hold request parameters.
22
+ * @var stdClass $params A static property to hold request parameters.
20
23
  *
21
24
  * This property is used to hold request parameters that are passed to the request.
22
25
  *
@@ -28,10 +31,10 @@ class Request
28
31
  * $id = Request::$params->id;
29
32
  * ```
30
33
  */
31
- public static \ArrayObject $params;
34
+ public static ArrayObject $params;
32
35
 
33
36
  /**
34
- * @var \stdClass $dynamicParams A static property to hold dynamic parameters.
37
+ * @var stdClass $dynamicParams A static property to hold dynamic parameters.
35
38
  *
36
39
  * This property is used to hold dynamic parameters that are passed to the request.
37
40
  *
@@ -53,10 +56,10 @@ class Request
53
56
  *
54
57
  * The above code will output the dynamic parameters as an array, which can be useful for debugging purposes.
55
58
  */
56
- public static \ArrayObject $dynamicParams;
59
+ public static ArrayObject $dynamicParams;
57
60
 
58
61
  /**
59
- * @var \stdClass $localStorage A static property to hold request parameters.
62
+ * @var stdClass $localStorage A static property to hold request parameters.
60
63
  *
61
64
  * This property is used to hold request parameters that are passed to the request.
62
65
  *
@@ -68,7 +71,7 @@ class Request
68
71
  * $id = Request::$localStorage->id;
69
72
  * ```
70
73
  */
71
- public static \ArrayObject $localStorage;
74
+ public static ArrayObject $localStorage;
72
75
 
73
76
  /**
74
77
  * @var mixed $data Holds request data (e.g., JSON body).
@@ -189,8 +192,8 @@ class Request
189
192
  */
190
193
  public static function init(): void
191
194
  {
192
- self::$params = new \ArrayObject([], \ArrayObject::ARRAY_AS_PROPS);
193
- self::$dynamicParams = new \ArrayObject([], \ArrayObject::ARRAY_AS_PROPS);
195
+ self::$params = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
196
+ self::$dynamicParams = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
194
197
 
195
198
  self::$referer = $_SERVER['HTTP_REFERER'] ?? 'Unknown';
196
199
  self::$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
@@ -286,15 +289,15 @@ class Request
286
289
  /**
287
290
  * Get the request parameters.
288
291
  *
289
- * @return \ArrayObject The request parameters as an \ArrayObject with properties.
292
+ * @return ArrayObject The request parameters as an ArrayObject with properties.
290
293
  */
291
- private static function getParams(): \ArrayObject
294
+ private static function getParams(): ArrayObject
292
295
  {
293
- $params = new \ArrayObject([], \ArrayObject::ARRAY_AS_PROPS);
296
+ $params = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
294
297
 
295
298
  switch (self::$method) {
296
299
  case 'GET':
297
- $params = new \ArrayObject($_GET, \ArrayObject::ARRAY_AS_PROPS);
300
+ $params = new ArrayObject($_GET, ArrayObject::ARRAY_AS_PROPS);
298
301
  break;
299
302
  default:
300
303
  // Handle JSON input with different variations (e.g., application/json, application/ld+json, etc.)
@@ -303,7 +306,7 @@ class Request
303
306
  if ($jsonInput !== false && !empty($jsonInput)) {
304
307
  self::$data = json_decode($jsonInput, true);
305
308
  if (json_last_error() === JSON_ERROR_NONE) {
306
- $params = new \ArrayObject(self::$data, \ArrayObject::ARRAY_AS_PROPS);
309
+ $params = new ArrayObject(self::$data, ArrayObject::ARRAY_AS_PROPS);
307
310
  } else {
308
311
  Boom::badRequest('Invalid JSON body')->toResponse();
309
312
  }
@@ -315,9 +318,9 @@ class Request
315
318
  $rawInput = file_get_contents('php://input');
316
319
  if ($rawInput !== false && !empty($rawInput)) {
317
320
  parse_str($rawInput, $parsedParams);
318
- $params = new \ArrayObject($parsedParams, \ArrayObject::ARRAY_AS_PROPS);
321
+ $params = new ArrayObject($parsedParams, ArrayObject::ARRAY_AS_PROPS);
319
322
  } else {
320
- $params = new \ArrayObject($_POST, \ArrayObject::ARRAY_AS_PROPS);
323
+ $params = new ArrayObject($_POST, ArrayObject::ARRAY_AS_PROPS);
321
324
  }
322
325
  }
323
326
  break;
@@ -330,28 +333,28 @@ class Request
330
333
  * Retrieves the local storage data from the session or initializes it if not present.
331
334
  *
332
335
  * This method checks if the local storage data is available in the static data array or the session.
333
- * If the data is found, it is decoded from JSON if necessary and returned as an \ArrayObject.
334
- * If the data is not found, an empty \ArrayObject is returned.
336
+ * If the data is found, it is decoded from JSON if necessary and returned as an ArrayObject.
337
+ * If the data is not found, an empty ArrayObject is returned.
335
338
  *
336
- * @return \ArrayObject The local storage data as an \ArrayObject.
339
+ * @return ArrayObject The local storage data as an ArrayObject.
337
340
  */
338
- private static function getLocalStorage(): \ArrayObject
341
+ private static function getLocalStorage(): ArrayObject
339
342
  {
340
- $sessionKey = 'appState_59E13';
341
- $localStorage = new \ArrayObject([], \ArrayObject::ARRAY_AS_PROPS);
343
+ $sessionKey = PrismaPHPSettings::$localStoreKey;
344
+ $localStorage = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
342
345
 
343
346
  if (isset(self::$data[$sessionKey])) {
344
347
  $data = self::$data[$sessionKey];
345
348
 
346
349
  if (is_array($data)) {
347
350
  $_SESSION[$sessionKey] = $data;
348
- $localStorage = new \ArrayObject($data, \ArrayObject::ARRAY_AS_PROPS);
351
+ $localStorage = new ArrayObject($data, ArrayObject::ARRAY_AS_PROPS);
349
352
  } else {
350
353
  $decodedData = json_decode($data, true);
351
354
 
352
355
  if (json_last_error() === JSON_ERROR_NONE) {
353
356
  $_SESSION[$sessionKey] = $data;
354
- $localStorage = new \ArrayObject($decodedData, \ArrayObject::ARRAY_AS_PROPS);
357
+ $localStorage = new ArrayObject($decodedData, ArrayObject::ARRAY_AS_PROPS);
355
358
  } else {
356
359
  Boom::badRequest('Invalid JSON body')->toResponse();
357
360
  }
@@ -361,12 +364,12 @@ class Request
361
364
  $sessionData = $_SESSION[$sessionKey];
362
365
 
363
366
  if (is_array($sessionData)) {
364
- $localStorage = new \ArrayObject($sessionData, \ArrayObject::ARRAY_AS_PROPS);
367
+ $localStorage = new ArrayObject($sessionData, ArrayObject::ARRAY_AS_PROPS);
365
368
  } else {
366
369
  $decodedData = json_decode($sessionData, true);
367
370
 
368
371
  if (json_last_error() === JSON_ERROR_NONE) {
369
- $localStorage = new \ArrayObject($decodedData, \ArrayObject::ARRAY_AS_PROPS);
372
+ $localStorage = new ArrayObject($decodedData, ArrayObject::ARRAY_AS_PROPS);
370
373
  }
371
374
  }
372
375
  }
@@ -436,24 +439,17 @@ class Request
436
439
  */
437
440
  public static function redirect(string $url, bool $replace = true, int $responseCode = 0): void
438
441
  {
439
- // Clean (discard) any previous output
440
442
  ob_clean();
441
-
442
- // Start a fresh output buffer
443
443
  ob_start();
444
444
 
445
445
  if (!self::$isWire && !self::$isAjax) {
446
- // Normal redirect for non-ajax/wire requests
447
- ob_end_clean(); // End the buffer, don't send it
448
- header("Location: $url", $replace, $responseCode); // Redirect using header
446
+ header("Location: $url", $replace, $responseCode);
449
447
  } else {
450
- // For ajax/wire requests, send the custom redirect response
451
- ob_clean(); // Clean any previous output
452
- echo "redirect_7F834=$url"; // Output the redirect message
453
- ob_end_flush(); // Flush and send the output buffer
448
+ ob_clean();
449
+ echo "redirect_7F834=$url";
450
+ ob_end_flush();
454
451
  }
455
452
 
456
- // Terminate the script to prevent any further output
457
453
  exit;
458
454
  }
459
455
  }
@@ -31,7 +31,7 @@ class StateManager
31
31
  * @param mixed $initialValue The initial value to set if the key does not exist.
32
32
  * @return mixed The state value for the specified key.
33
33
  */
34
- public static function getState(string $key = null, mixed $initialValue = null): mixed
34
+ public static function getState(?string $key = null, mixed $initialValue = null): mixed
35
35
  {
36
36
  if ($key === null) {
37
37
  return new \ArrayObject(self::$state, \ArrayObject::ARRAY_AS_PROPS);
@@ -100,7 +100,7 @@ class StateManager
100
100
  *
101
101
  * @param string|null $key The key of the state value to reset.
102
102
  */
103
- public static function resetState(string $key = null): void
103
+ public static function resetState(?string $key = null): void
104
104
  {
105
105
  if ($key !== null) {
106
106
  if (array_key_exists($key, self::$state)) {
@@ -6,6 +6,8 @@ namespace Lib\Websocket;
6
6
 
7
7
  use Ratchet\MessageComponentInterface;
8
8
  use Ratchet\ConnectionInterface;
9
+ use Exception;
10
+ use SplObjectStorage;
9
11
 
10
12
  class ConnectionManager implements MessageComponentInterface
11
13
  {
@@ -13,7 +15,7 @@ class ConnectionManager implements MessageComponentInterface
13
15
 
14
16
  public function __construct()
15
17
  {
16
- $this->clients = new \SplObjectStorage;
18
+ $this->clients = new SplObjectStorage;
17
19
  }
18
20
 
19
21
  public function onOpen(ConnectionInterface $conn)
@@ -33,7 +35,7 @@ class ConnectionManager implements MessageComponentInterface
33
35
  echo "Connection {$conn->resourceId} has disconnected";
34
36
  }
35
37
 
36
- public function onError(ConnectionInterface $conn, \Exception $e)
38
+ public function onError(ConnectionInterface $conn, Exception $e)
37
39
  {
38
40
  echo "An error has occurred: {$e->getMessage()}";
39
41
  $conn->close();
@@ -1,4 +1,4 @@
1
- @import "tailwindcss";
1
+ @import "tailwindcss" source("../../../src/");
2
2
 
3
3
  @layer base {
4
4
  /* Define fonts */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "2.0.0-alpha.9",
3
+ "version": "2.0.0-beta.2",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,37 +0,0 @@
1
- // This is your Prisma schema file,
2
- // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
-
4
- // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
5
- // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
6
-
7
- generator client {
8
- provider = "prisma-client-js"
9
- }
10
-
11
- datasource db {
12
- provider = "postgresql"
13
- url = env("DATABASE_URL")
14
- }
15
-
16
- model User {
17
- id String @id @default(cuid())
18
- name String?
19
- email String? @unique
20
- password String?
21
- emailVerified DateTime?
22
- image String?
23
- createdAt DateTime @default(now())
24
- updatedAt DateTime @updatedAt
25
-
26
- roleId Int?
27
- userRole UserRole? @relation(fields: [roleId], references: [id])
28
-
29
- @@map("Users")
30
- }
31
-
32
- model UserRole {
33
- id Int @id @default(autoincrement())
34
- name String @unique
35
-
36
- user User[]
37
- }