├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .phpcs.xml ├── assets ├── index.css └── index.js ├── build.sh ├── composer.json ├── install.sh ├── plugin.php ├── readme.md ├── readme.txt ├── src ├── Admin.php ├── License.php ├── Log.php ├── Modules │ └── ModuleX │ │ └── Init.php ├── Plugin.php ├── Schema.php └── Traits │ ├── AjaxRequest.php │ ├── Api.php │ ├── FileSystem.php │ ├── Orm.php │ └── Singleton.php └── strings.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: dev_kabir 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: devkabir 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | *.zip 4 | .idea -------------------------------------------------------------------------------- /.phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Coding standard for Dev Kabir's Projects 4 | 5 | plugin.php 6 | src 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /assets/index.css: -------------------------------------------------------------------------------- 1 | div.notice { 2 | display: none !important; 3 | } -------------------------------------------------------------------------------- /assets/index.js: -------------------------------------------------------------------------------- 1 | alert('Script Loaded') 2 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Function to display progress messages 4 | progress_message() { 5 | local message="$1" 6 | 7 | # Define color codes 8 | local color_reset="\033[0m" 9 | local color_green="\033[32m" 10 | 11 | # Print the colorized message 12 | echo -e "[$(date +'%Y-%m-%d %H:%M:%S')] ${color_green}${message}${color_reset}" 13 | } 14 | 15 | # abort on errors 16 | set -e 17 | 18 | # prepare place for build. 19 | plugin_name="$(basename $PWD)" 20 | progress_message "Preparing build directory..." 21 | rm -rf ./"$plugin_name" ./"$plugin_name".zip 22 | mkdir ./"$plugin_name" 23 | 24 | # copy all files for production 25 | progress_message "Copying files for production..." 26 | cp -R ./composer.json ./*.php src assets readme.txt ./"$plugin_name"/ --parents 27 | 28 | # Install PHP dependencies 29 | progress_message "Installing PHP dependencies..." 30 | composer install --working-dir=./"$plugin_name" --no-dev 31 | rm ./"$plugin_name"/composer.json 32 | 33 | # Add index.php to every directory 34 | progress_message "Adding index.php to every directory..." 35 | find ./"$plugin_name" -type d -exec sh -c "echo ' {}/index.php" \; 36 | 37 | # Create zip archive 38 | progress_message "Creating zip archive..." 39 | # TODO: update zip path based on your OS. 40 | "C:\Program Files\7-Zip\7z.exe" a ./"$plugin_name".zip ./"$plugin_name"/* 41 | 42 | # Revert changes for production 43 | progress_message "Reverting changes..." 44 | rm -rf ./"$plugin_name" 45 | 46 | # Completion message 47 | progress_message "Build process completed successfully." 48 | exit 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devkabir/your-plugin-name", 3 | "description": "A simple oop structure in the singleton pattern for a WordPress plugin.", 4 | "version": "1.0.0", 5 | "type": "project", 6 | "license": "GPL-2.0-or-later", 7 | "authors": [ 8 | { 9 | "name": "Dev Kabir", 10 | "email": "dev.kabir01@gmail.com" 11 | } 12 | ], 13 | "minimum-stability": "dev", 14 | "prefer-stable": true, 15 | "require-dev": { 16 | "php-stubs/woocommerce-stubs": "^9.3", 17 | "php-stubs/wordpress-stubs": "^6.6", 18 | "wp-coding-standards/wpcs": "^3.0" 19 | }, 20 | "scripts": { 21 | "lint": "phpcs src --standard=.phpcs.xml -s --report=source", 22 | "format": "phpcbf src --standard=.phpcs.xml -s --report=source", 23 | "post-create-project-cmd": [ 24 | "bash ./install.sh" 25 | ] 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "PluginPackage\\": "src/" 30 | } 31 | }, 32 | "config": { 33 | "allow-plugins": { 34 | "dealerdirect/phpcodesniffer-composer-installer": true 35 | }, 36 | "platform-check": true, 37 | "optimize-autoloader": true, 38 | "sort-packages": true, 39 | "lock": false 40 | }, 41 | "require": { 42 | "php": "^7.4 || ^8.0", 43 | "ext-json": "*" 44 | } 45 | } -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to display progress messages 4 | progress_message() { 5 | local message="$1" 6 | 7 | # Define color codes 8 | local color_reset="\033[0m" 9 | local color_green="\033[32m" 10 | 11 | # Print the colorized message 12 | echo -e "[$(date +'%Y-%m-%d %H:%M:%S')] ${color_green}${message}${color_reset}" 13 | } 14 | 15 | # abort on errors 16 | set -e 17 | progress_message "Replacing placeholders..." 18 | # Get the current directory name 19 | base=${PWD##*/} 20 | # Convert to Pascal case for namespace 21 | namespace=$(sed -E 's/(^|-)([a-zA-Z0-9])/\U\2/g' <<< "$base") 22 | # Convert to Title Case with spaces 23 | title=$(sed 's/-/ /g' <<< "$base") 24 | title=${title^} 25 | object=$(sed 's/-/_/g' <<< "$base") 26 | 27 | if [ -e "plugin.php" ]; then 28 | mv plugin.php "$base.php" 29 | fi 30 | # Search and replace "Your Plugin Name" with the current directory name in all PHP, JS, JSON, and HTML files except files in the vendor folder inside src directory 31 | find ./ -type f \( -name "*.php" -o -name "*.js" -o -name "*.json" -o -name "*.html" -o -name "*.txt" \) -not -path "./vendor/*" -not -path "./assets/node_modules/*" -execdir sed -i "s/Your Plugin Name/${title//\//\\/}/g" {} \; 32 | # Search and replace "your-plugin-name" with the current directory name in all PHP, JS, JSON, and HTML files except files in the vendor folder inside src directory 33 | find ./ -type f \( -name "*.php" -o -name "*.js" -o -name "*.json" -o -name "*.html" \) -not -path "./vendor/*" -not -path "./assets/node_modules/*" -execdir sed -i "s/your-plugin-name/${base//\//\\/}/g" {} \; 34 | # Search and replace "YourPluginName" with the current directory name in all PHP, JS, JSON, and HTML files except files in the vendor folder inside src directory 35 | find ./ -type f \( -name "*.php" -o -name "*.js" -o -name "*.json" -o -name "*.html" \) -not -path "./vendor/*" -not -path "./assets/node_modules/*" -execdir sed -i "s/PluginPackage/${namespace//\//\\/}/g" {} \; 36 | # Search and replace "your_plugin_name" with the current directory name in all PHP, JS, JSON, and HTML files except files in the vendor folder inside src directory 37 | find ./ -type f \( -name "*.php" -o -name "*.js" -o -name "*.json" -o -name "*.html" -o -name "*.ts" \) -not -path "./vendor/*" -not -path "./assets/node_modules/*" -execdir sed -i "s/your_plugin_name/${object//\//\\/}/g" {} \; 38 | # run composer dump-autoload 39 | progress_message "Running composer dump-autoload..." 40 | composer dump-autoload 41 | # delete this file 42 | progress_message "Deleting unnecessary files..." 43 | rm -rf ./.github 44 | rm ./readme.md 45 | rm ./install.sh 46 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | 26 | ``` 27 | 28 | 3. Replace `` with what you want to call your plugin. 29 | 4. Hit **Enter** and let it do its magic! 30 | 5. You’re ready to start creating something amazing for WordPress! 31 | 32 | Happy coding! 33 | 34 | ## Building Your Plugin (Creating a ZIP File) 35 | 36 | When you're done and want to share your plugin, follow these steps to make a ZIP file: 37 | 38 | 1. In your terminal, run the following command: 39 | 40 | ```bash 41 | ./build.sh 42 | ``` 43 | 44 | 2. This command will create a **ZIP** file of your plugin, so you can upload it to WordPress or share it with others. 45 | 46 | ## Extra Plugins (Recommended) 47 | 48 | If you want some extra tools to help with debugging (fixing errors), you can use [Zero Debugger](https://github.com/devkabir/0-debugger). 49 | 50 | ## Want More? Check Out Other Branches. 51 | 52 | - [Vue based Admin Template](https://github.com/devkabir/wordpress-plugin/tree/vue-admin-template) 53 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Your Plugin Name === 2 | Contributors: (this should be a list of wordpress.org userid's) 3 | Donate link: https://example.com/ 4 | Tags: tag1, tag2 5 | Requires at least: 5.3 6 | Tested up to: 6.3.1 7 | Stable tag: 4.7 8 | Requires PHP: 7.1 9 | License: GPLv2 or later 10 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 | 12 | Here is a short description of the plugin. This should be no more than 150 characters. No markup here. 13 | 14 | == Description == 15 | 16 | This is the long description. No limit, and you can use Markdown (as well as in the following sections). 17 | 18 | List: 19 | 20 | * "Contributors" is a comma separated list of wordpress.org usernames 21 | * "Tags" is a comma separated list of tags that apply to the plugin 22 | * "Requires at least" is the lowest version that the plugin will work on 23 | * "Tested up to" is the highest version that you've *successfully used to test the plugin* 24 | * Stable tag must indicate the Subversion "tag" of the latest stable version 25 | 26 | If no stable tag is provided, your users may not get the correct version of your code. 27 | 28 | == Frequently Asked Questions == 29 | 30 | = A question that someone might have = 31 | 32 | An answer to that question. 33 | 34 | = What about foo bar? = 35 | 36 | Answer to foo bar dilemma. 37 | 38 | == Screenshots == 39 | 40 | 1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Screenshots are stored in the /assets directory. 41 | 2. This is the second screen shot 42 | 43 | == Changelog == 44 | 45 | = 1.0.0 - date = 46 | * Initial release 47 | -------------------------------------------------------------------------------- /src/Admin.php: -------------------------------------------------------------------------------- 1 | Hello Dev!' ); 51 | }, 52 | ); 53 | } 54 | ); 55 | add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) ); 56 | // Fire modules. 57 | $this->modules(); 58 | } 59 | 60 | /** 61 | * It initializes all the modules that are needed to run the plugin. 62 | * 63 | * @return void 64 | */ 65 | public function modules() { 66 | // TODO: Add new modules here. 67 | ModuleX::init(); 68 | } 69 | /** 70 | * It loads scripts based on plugin's mode, dev or prod. 71 | * 72 | * @return void 73 | */ 74 | public function scripts(): void { 75 | wp_register_style( SLUG, plugins_url( 'assets/index.css', FILE ), array(), VERSION ); 76 | wp_register_script( SLUG, plugins_url( 'assets/index.js', FILE ), array(), VERSION, true ); 77 | wp_localize_script( 78 | SLUG, 79 | str_replace( '-', '_', SLUG ), 80 | array( 81 | 'security_token' => wp_create_nonce( 'wp_rest' ), 82 | 'strings' => $this->strings(), 83 | ), 84 | ); 85 | } 86 | 87 | /** 88 | * It returns the sanitized i18n strings array for js templates. 89 | * 90 | * @return array The sanitized strings array. 91 | */ 92 | private function strings() { 93 | $string = include plugin_dir_path( FILE ) . 'strings.php'; 94 | return $this->sanitize_array( $string ); 95 | } 96 | 97 | /** 98 | * Sanitizes all values in a multidimensional array recursively. 99 | * 100 | * @param array $data The array to sanitize. 101 | * @return array The sanitized array. 102 | */ 103 | private function sanitize_array( array &$data ): array { 104 | foreach ( $data as &$value ) { 105 | if ( ! is_array( $value ) ) { 106 | $value = esc_attr( sanitize_text_field( $value ) ); 107 | } else { 108 | $value = $this->sanitize_array( $value ); 109 | } 110 | } 111 | unset( $value ); // Unset the reference to avoid potential bugs. 112 | return $data; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/License.php: -------------------------------------------------------------------------------- 1 | $site_url, 38 | 'event' => $event, 39 | 'version' => VERSION, 40 | 'name' => SLUG, 41 | ); 42 | 43 | $headers = array( 44 | 'user-agent' => SLUG . ';' . password_hash( $site_url, PASSWORD_BCRYPT ), 45 | 'Accept' => 'application/json', 46 | 'Content-Type' => 'application/json', 47 | 'Origin' => $site_url, 48 | 'Referer' => $site_url, 49 | 'Cache-Control' => 'no-cache', 50 | ); 51 | 52 | $response = wp_remote_post( 53 | $this->api, 54 | array( 55 | 'timeout' => 30, 56 | 'redirection' => 5, 57 | 'httpversion' => '1.0', 58 | 'headers' => $headers, 59 | 'body' => wp_json_encode( $data ), 60 | 'sslverify' => false, 61 | 'cookies' => array(), 62 | ) 63 | ); 64 | 65 | if ( false === is_wp_error( $response ) && false === get_option( SLUG . '-license-key' ) ) { 66 | $response = wp_remote_retrieve_body( $response ); 67 | update_option( SLUG . '-license-key', $response ); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Log.php: -------------------------------------------------------------------------------- 1 | file( $type ); 26 | 27 | if ( is_array( $message ) || is_object( $message ) || is_iterable( $message ) ) { 28 | $message = wp_json_encode( $message, JSON_PRETTY_PRINT ); 29 | } else { 30 | $decoded = json_decode( $message, true ); 31 | if ( json_last_error() === JSON_ERROR_NONE ) { 32 | $message = wp_json_encode( $decoded, JSON_PRETTY_PRINT ); 33 | } 34 | } 35 | $date = sprintf( '[%s]::', gmdate( 'd-M-Y h:i:s A' ) ); 36 | $message = $this->read( $type ) . PHP_EOL . $date . sanitize_textarea_field( $message ); 37 | 38 | return $this->write_file( $file, $message ); 39 | } 40 | 41 | 42 | /** 43 | * It's reading the log file and returning the content. 44 | * 45 | * @param string $type log type. 46 | * 47 | * @return string logs 48 | */ 49 | public function read( string $type ) { 50 | $file = $this->file( $type ); 51 | 52 | return $this->read_file( $file ); 53 | } 54 | 55 | 56 | /** 57 | * It's deleting the log file. 58 | * 59 | * @param string $type log type. 60 | * 61 | * @return bool 62 | */ 63 | public function delete( string $type ) { 64 | $file = $this->file( $type ); 65 | return $this->delete_file( $file ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Modules/ModuleX/Init.php: -------------------------------------------------------------------------------- 1 | create(); 27 | } 28 | 29 | /** 30 | * Handle the plugin deactivation event. 31 | * 32 | * In this method you can add any code that needs to be executed when the plugin is deactivated. For example, 33 | * deleting tables, options, etc. 34 | * 35 | * @return void 36 | */ 37 | public static function deactivate() { 38 | License::instance( 'deactivate' ); 39 | Schema::instance()->truncate(); 40 | } 41 | 42 | /** 43 | * Handle the plugin uninstall event. 44 | * 45 | * In this method you can add any code that needs to be executed when the plugin is uninstalled. For example, 46 | * deleting tables, options, etc. 47 | * 48 | * @return void 49 | */ 50 | public static function uninstall() { 51 | License::instance( 'uninstall' ); 52 | Schema::instance()->delete(); 53 | } 54 | 55 | /** 56 | * Initialize the plugin by setting up necessary components. 57 | * This method is called automatically when the plugin is activated. 58 | * 59 | * @return void 60 | */ 61 | public static function init() { 62 | if ( is_admin() ) { 63 | Admin::instance(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Schema.php: -------------------------------------------------------------------------------- 1 | "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}table_name ( 25 | id INT(11) NOT NULL AUTO_INCREMENT, 26 | column_name VARCHAR(255) NOT NULL, 27 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 28 | updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 29 | PRIMARY KEY (id) 30 | );", 31 | ); 32 | 33 | foreach ( $tables as $sql ) { 34 | $wpdb->query( $sql ); 35 | if ( ! empty( $wpdb->last_error ) ) { 36 | Log::instance()->write( 37 | 'error', 38 | array( 39 | 'error' => $wpdb->last_error, 40 | 'query' => $sql, 41 | ) 42 | ); 43 | break; 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * Delete tables in database. 50 | * 51 | * @return void 52 | */ 53 | public function delete() { 54 | global $wpdb; 55 | 56 | $tables = array( 57 | 'table_name' => "DROP TABLE IF EXISTS {$wpdb->prefix}table_name", 58 | ); 59 | 60 | foreach ( $tables as $sql ) { 61 | $wpdb->query( $sql ); 62 | if ( ! empty( $wpdb->last_error ) ) { 63 | Log::instance()->write( 64 | 'error', 65 | array( 66 | 'error' => $wpdb->last_error, 67 | 'query' => $sql, 68 | ) 69 | ); 70 | break; 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * Truncate tables in database. 77 | * 78 | * @return void 79 | */ 80 | public function truncate() { 81 | global $wpdb; 82 | 83 | $tables = array( 84 | 'table_name' => "TRUNCATE TABLE {$wpdb->prefix}table_name", 85 | ); 86 | 87 | foreach ( $tables as $sql ) { 88 | $wpdb->query( $sql ); 89 | if ( ! empty( $wpdb->last_error ) ) { 90 | Log::instance()->write( 91 | 'error', 92 | array( 93 | 'error' => $wpdb->last_error, 94 | 'query' => $sql, 95 | ) 96 | ); 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Traits/AjaxRequest.php: -------------------------------------------------------------------------------- 1 | errors = $message; 41 | return $this; 42 | } 43 | /** 44 | * Sets the data in the response array. 45 | * 46 | * @param mixed $data The data to be set. 47 | * @return self Returns the current object. 48 | */ 49 | private function set_data( $data ): self { 50 | $this->response = $data; 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Serve data to AJAX request. 57 | */ 58 | private function serve(): void { 59 | if ( empty( $this->errors ) ) { 60 | wp_send_json_success( $this->response ); 61 | } 62 | wp_send_json_error( $this->errors ); 63 | } 64 | 65 | /** 66 | * Verify and validate AJAX request. 67 | * 68 | * @param array $fields The fields to verify. 69 | * @param string $key The key to use for verification. Default is 'data'. 70 | */ 71 | private function verify( array $fields = array(), string $key = 'data' ): void { 72 | check_ajax_referer( SLUG, 'security_token' ); 73 | $this->response = array(); 74 | $this->errors = array(); 75 | if ( ! empty( $fields ) ) { 76 | // Will sanitized data before use. 77 | $un_slashed = array_map( 'wp_unslash', $_REQUEST[ $key ] ?? array() ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput 78 | $data = array_column( $un_slashed, 'value', 'name' ); 79 | $filtered = array_filter( 80 | $data, 81 | function ( $value, $key ) { 82 | switch ( $key ) { 83 | case 'email': 84 | return sanitize_email( $value ); 85 | case 'comment': 86 | case 'message': 87 | return sanitize_textarea_field( $value ); 88 | default: 89 | return sanitize_text_field( $value ); 90 | } 91 | }, 92 | ARRAY_FILTER_USE_BOTH 93 | ); 94 | $this->data = $this->extract_data( $filtered, $fields ); 95 | } 96 | } 97 | 98 | /** 99 | * Extracts data from an array using the given keys. 100 | * 101 | * @param array $sanitized The array from which to extract data. 102 | * @param array $keys The keys to use for extraction. 103 | * 104 | * @return array The extracted data. 105 | */ 106 | public function extract_data( array $sanitized, array $keys ): array { 107 | return array_intersect_key( $sanitized, array_flip( $keys ) ); 108 | } 109 | 110 | /** 111 | * Generate AJAX action for based on logged-in users. 112 | * 113 | * @param string $action The action to register. 114 | */ 115 | public function action( string $action ): string { 116 | if ( is_user_logged_in() ) { 117 | return sprintf( 'wp_ajax_%s-%s', SLUG, $action ); 118 | } 119 | return sprintf( 'wp_ajax_nopriv_%s-%s', SLUG, $action ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Traits/Api.php: -------------------------------------------------------------------------------- 1 | true, 27 | 'message' => '', 28 | 'data' => array(), 29 | ); 30 | 31 | 32 | /** 33 | * Retrieves the URL for the API endpoint. 34 | * 35 | * @return string The URL for the API endpoint. 36 | */ 37 | public static function get_url() { 38 | return get_rest_url() . self::$namespace . '/' . self::$route; 39 | } 40 | 41 | 42 | /** 43 | * Check if a given request has access to get data from custom table 44 | * 45 | * @return WP_Error|bool 46 | */ 47 | public function options_permissions_check() { 48 | if ( ! current_user_can( 'manage_options' ) ) { 49 | return new WP_Error( 50 | 'rest_forbidden', 51 | __( 'Sorry, you are not allowed to use this endpoint', 'your-plugin-name' ), 52 | array( 'status' => rest_authorization_required_code() ) 53 | ); 54 | } 55 | 56 | return true; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Traits/FileSystem.php: -------------------------------------------------------------------------------- 1 | get_dir() . $type . '.' . $extension; 22 | } 23 | 24 | 25 | /** 26 | * Initialize the WP file system. 27 | * 28 | * @return mixed 29 | */ 30 | private function load_filesystem() { 31 | global $wp_filesystem; 32 | 33 | if ( empty( $wp_filesystem ) ) { 34 | require_once ABSPATH . '/wp-admin/includes/file.php'; 35 | WP_Filesystem(); 36 | } 37 | } 38 | /** 39 | * Retrieves the directory path for the message files. 40 | * 41 | * @return string The directory path for the message files. 42 | * @global \WP_Filesystem_Direct $wp_filesystem 43 | */ 44 | private function get_dir(): string { 45 | $this->load_filesystem(); 46 | global $wp_filesystem; 47 | $upload_dir = wp_upload_dir(); 48 | $message_dir = $upload_dir['basedir'] . '/' . SLUG . '-files/'; 49 | if ( ! file_exists( $message_dir ) ) { 50 | $wp_filesystem->mkdir( $message_dir ); 51 | } 52 | 53 | return $message_dir; 54 | } 55 | 56 | /** 57 | * Reads the contents of a file. 58 | * 59 | * @param string $path The path to the file to read. 60 | * 61 | * @return string|false The contents of the file, or false if the file does not exist. 62 | * @global \WP_Filesystem_Direct $wp_filesystem 63 | */ 64 | private function read_file( $path ) { 65 | $this->load_filesystem(); 66 | global $wp_filesystem; 67 | if ( ! file_exists( $path ) ) { 68 | return false; 69 | } 70 | $this->load_filesystem(); 71 | return $wp_filesystem->get_contents( $path ); 72 | } 73 | 74 | /** 75 | * Writes the given content to the given file. 76 | * 77 | * @param string $path The path to the file to write to. 78 | * @param string $content The content to write to the file. 79 | * 80 | * @return bool True on success, false on failure. 81 | * @global \WP_Filesystem_Direct $wp_filesystem 82 | */ 83 | private function write_file( $path, $content ) { 84 | $this->load_filesystem(); 85 | global $wp_filesystem; 86 | return $wp_filesystem->put_contents( $path, $content ); 87 | } 88 | 89 | /** 90 | * Deletes the given file. 91 | * 92 | * @param string $path The path to the file to delete. 93 | * 94 | * @return bool True on success, false on failure. 95 | * @global \WP_Filesystem_Direct $wp_filesystem 96 | */ 97 | private function delete_file( $path ) { 98 | $this->load_filesystem(); 99 | global $wp_filesystem; 100 | if ( ! file_exists( $path ) ) { 101 | return false; 102 | } 103 | return $wp_filesystem->delete( $path ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Traits/Orm.php: -------------------------------------------------------------------------------- 1 | prefix . self::$table; 19 | } 20 | 21 | /** 22 | * Insert a new record into the database. 23 | * 24 | * @param array $data Associative array of column data to insert. 25 | * @return array Array of success status and ID of inserted record. 26 | */ 27 | public static function insert( array $data ): array { 28 | global $wpdb; 29 | $table_name = self::get_table_name(); 30 | $wpdb->insert( $table_name, $data ); 31 | if ( empty( $wpdb->last_error ) ) { 32 | return array( 33 | 'success' => true, 34 | 'id' => $wpdb->insert_id, 35 | ); 36 | } 37 | return array( 38 | 'success' => false, 39 | 'message' => $wpdb->last_error, 40 | 'query' => $wpdb->last_query, 41 | ); 42 | } 43 | 44 | /** 45 | * Update records in the database. 46 | * 47 | * @param array $data Associative array of column data to update. 48 | * @param array $where Associative array of column data for WHERE clause. 49 | * @return array Array of success status and ID of inserted record. 50 | */ 51 | public static function update( array $data, array $where ): array { 52 | global $wpdb; 53 | $table_name = self::get_table_name(); 54 | $wpdb->update( $table_name, $data, $where ); 55 | if ( empty( $wpdb->last_error ) ) { 56 | return array( 57 | 'success' => true, 58 | 'id' => $wpdb->insert_id, 59 | ); 60 | } 61 | return array( 62 | 'success' => false, 63 | 'message' => $wpdb->last_error, 64 | 'query' => $wpdb->last_query, 65 | ); 66 | } 67 | 68 | /** 69 | * Delete records from the database. 70 | * 71 | * @param array $where Associative array of column data for WHERE clause. 72 | * @return array Array of success status and ID of inserted record. 73 | */ 74 | public static function delete( array $where ): array { 75 | global $wpdb; 76 | $table_name = self::get_table_name(); 77 | $wpdb->delete( $table_name, $where ); 78 | if ( empty( $wpdb->last_error ) ) { 79 | return array( 80 | 'success' => true, 81 | 'id' => $wpdb->insert_id, 82 | ); 83 | } 84 | return array( 85 | 'success' => false, 86 | 'message' => $wpdb->last_error, 87 | 'query' => $wpdb->last_query, 88 | ); 89 | } 90 | 91 | /** 92 | * Select records from the database. 93 | * 94 | * @param array $where Associative array of column data for WHERE clause. 95 | * @param array $columns Optional. Columns to select. Default is empty array. 96 | * @return array Database query results or null if none. 97 | */ 98 | public static function select( $where = array(), $columns = array() ) { 99 | global $wpdb; 100 | $table_name = self::get_table_name(); 101 | 102 | if ( empty( $columns ) ) { 103 | $columns = '*'; 104 | } else { 105 | $columns = implode( ', ', $columns ); 106 | } 107 | 108 | $sql = "SELECT $columns FROM $table_name WHERE 1=1"; 109 | if ( empty( $where ) ) { 110 | $prepared_sql = $sql; 111 | } else { 112 | foreach ( $where as $key => $value ) { 113 | $sql .= " AND $key = %s"; 114 | } 115 | $prepared_sql = $wpdb->prepare( $sql, array_values( $where ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 116 | } 117 | $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 118 | if ( empty( $wpdb->last_error ) ) { 119 | return array( 120 | 'success' => true, 121 | 'data' => $wpdb->last_result, 122 | ); 123 | } 124 | return array( 125 | 'success' => false, 126 | 'message' => $wpdb->last_error, 127 | 'query' => $wpdb->last_query, 128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Traits/Singleton.php: -------------------------------------------------------------------------------- 1 |