├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.md │ └── FEATURE_REQUEST.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Classes │ ├── Enum.php │ ├── MethodHelper.php │ └── ProvidesClassInfo.php ├── Functions │ └── generic.php └── InternalHelpers.php └── tests ├── TestCase.php └── Unit ├── Classes ├── ConstantsHelperTest.php ├── MethodHelperTest.php └── ProvidesClassInfoTest.php └── Functions └── GenericHelpersTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.yml] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve this project 4 | title: '' 5 | labels: bug 6 | assignees: sebastiaanluca 7 | --- 8 | 9 | ### Description 10 | 11 | ### Expected result 12 | 13 | ### Steps to reproduce 14 | 15 | 1. 16 | 2. 17 | 3. 18 | 19 | ### Traces 20 | 21 | Logs, error output, etc. 22 | 23 | ### Environment information 24 | 25 | Setup, environment, packages, versions, etc. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature 6 | assignees: sebastiaanluca 7 | --- 8 | 9 | ### Description 10 | 11 | A clear and concise description of the problem or proposal. 12 | 13 | ### Suggested solution 14 | 15 | A clear and concise description of what you want to happen. 16 | 17 | ### Possible alternatives 18 | 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | ### Additional context 22 | 23 | Any other context or screenshots to help situate and understand the requested feature. 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pull request 3 | about: Create a new pull request to merge code into the main branch 4 | title: 'A short, descriptive title' 5 | labels: '' 6 | assignees: sebastiaanluca 7 | --- 8 | 9 | ## PR Type 10 | 11 | What kind of pull request is this? Put an `x` in all the boxes that apply: 12 | 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Extend feature (non-breaking change which extends existing functionality) 16 | - [ ] Change feature (non-breaking change which either changes or refactors existing functionality) 17 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 18 | 19 | --- 20 | 21 | ### Description 22 | 23 | Clearly describe what this pull request changes and why. 24 | 25 | ### Steps to follow to verify functionality 26 | 27 | 1. Clearly state which actions should be performed to fully and correctly review this issue. 28 | 2. … 29 | 30 | ### Related issues 31 | 32 | Link to the issue(s) this pull request handles. 33 | 34 | ### Related PRs 35 | 36 | Link to any related pull requests. 37 | 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * *' 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | php: [8.0] 16 | laravel: [8.*] 17 | dependency-version: [prefer-lowest, prefer-stable] 18 | os: [ubuntu-latest] 19 | include: 20 | - laravel: 8.* 21 | testbench: 6.* 22 | 23 | name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} 24 | 25 | steps: 26 | - name: Check out code 27 | uses: actions/checkout@v2 28 | 29 | - name: Cache dependencies 30 | uses: actions/cache@v2 31 | with: 32 | path: ~/.composer/cache/files 33 | key: dependencies-${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 34 | 35 | - name: Validate Composer configuration file 36 | run: composer validate --strict 37 | 38 | - name: Set up PHP 39 | uses: shivammathur/setup-php@v2 40 | with: 41 | php-version: ${{ matrix.php }} 42 | extensions: mbstring 43 | coverage: none 44 | 45 | - name: Install dependencies 46 | run: | 47 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction 48 | 49 | - name: Execute tests 50 | run: vendor/bin/phpunit 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | composer.lock 3 | vendor 4 | tests/files/ 5 | /.idea 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All Notable changes to `sebastiaanluca/php-helpers` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 6 | 7 | ## 3.0.0 (2021-03-11) 8 | 9 | ### Added 10 | 11 | - Added support for PHP 8 12 | 13 | ### Removed 14 | 15 | - Dropped support for PHP 7.4 and lower 16 | - Dropped support for Carbon 1.x 17 | - Removed Kint package requirement 18 | - Removed debug helpers 19 | 20 | ## 2.0.0 (2019-02-27) 21 | 22 | ### Changed 23 | 24 | - Renamed `Constants` to `Enum` to better reflect their purpose 25 | - Upgraded to PHPUnit 8 26 | - Test against PHP 7.3 and 7.4 27 | 28 | ## 1.0.0 (2018-07-22) 29 | 30 | ### Added 31 | 32 | - rand_bool helper 33 | - str_wrap helper 34 | - is\_assoc\_array helper 35 | - array_expand helper 36 | - array_without helper 37 | - array\_pull\_value helper 38 | - array\_pull\_values helper 39 | - array_hash helper 40 | - object_hash helper 41 | - has\_public\_method helper 42 | - carbon helper 43 | - create\_temporary\_file helper 44 | - sss helper 45 | - ddd helper 46 | - sss_if helper 47 | - ddd_if helper 48 | - Constants trait 49 | - ProvidesClassInfo trait 50 | - MethodHelper class 51 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | It's simple, really: 4 | 5 | - Everyone is welcome to contribute 6 | - Use common sense at all times 7 | - Be open to other opinions and constructive criticism 8 | - Be friendly 9 | 10 | Feel like someone's in violation of this? [Contact me directly][link-author-email]. 11 | 12 | [link-author-email]: mailto:hello@sebastiaanluca.com 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are very welcome and will be fully credited. 4 | 5 | We accept contributions via pull requests. 6 | 7 | ## Pull request guidelines 8 | 9 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Don't go crazy with the formatting. 10 | 11 | - **Add tests** - Your patch won't be accepted if it doesn't have tests. Don't worry though! Feel free to submit a PR without, we'll help you along the way. 12 | 13 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 14 | 15 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 16 | 17 | - **Create feature branches** - Don't ask us to pull from your master branch unless it only contains the PR code. 18 | 19 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 20 | 21 | - **Send coherent history** - Make sure each commit in your pull request is somewhat meaningful and contains related changes. Don't go overboard by changing a dozen files and doing everything in a single commit. 22 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Make it clear if the issue is a **bug**, an **enhancement** or just a **question**. The easiest way to indicate this is to prefix the title, e.g. `[Question] I have a question`. 4 | 5 | Provide a detailed description of the change or addition you are proposing. Include some screenshots or code examples if possible. 6 | 7 | ### Your environment 8 | 9 | If you're reporting a bug or asking a specific question, include as many relevant details about your environment so we can reproduce it. The more, the better. 10 | 11 | - Package version or last commit 12 | - Operating system and version 13 | - PHP version 14 | - Related package versions 15 | - … 16 | 17 | ## Context 18 | 19 | Why is this change important to you? How would you use it? How can it benefit other users? 20 | 21 | ## Possible implementation 22 | 23 | Not obligatory, but suggest an idea for implementing addition or change. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2018 (until present) Sebastiaan Luca 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Type 2 | 3 | What kind of pull request is this? Put an `x` in all the boxes that apply: 4 | 5 | - [ ] Bug fix (non-breaking change which fixes an issue) 6 | - [ ] New feature (non-breaking change which adds functionality) 7 | - [ ] Extend feature (non-breaking change which extends existing functionality) 8 | - [ ] Change feature (non-breaking change which either changes or refactors existing functionality) 9 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 10 | 11 | ## What does it change? 12 | 13 | Describe your changes in detail. 14 | 15 | ## Why this PR? 16 | 17 | Why is this change required? What problem does it solve? 18 | 19 | ## How has this been tested? 20 | 21 | Please describe in detail how you tested your changes (or are planning on testing them). 22 | 23 | ## Checklist 24 | 25 | To facilitate merging your change and the approval of this PR, please make sure you've reviewed and applied the following: 26 | 27 | - This PR addresses exactly one issue 28 | - All changes were made in a fork of this project (preferably also in a separate branch) 29 | - It follows the code style of this project 30 | - Tests were added to cover the changes 31 | - All previously existing tests still pass 32 | - If the change to the code requires a change to the documentation, it has been updated accordingly 33 | 34 | If you're unsure about any of these, don't hesitate to ask. We're here to help! 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An extensive set of PHP helper functions and classes 2 | 3 | [![Latest stable release][version-badge]][link-packagist] 4 | [![Software license][license-badge]](LICENSE.md) 5 | [![Build status][githubaction-badge]][link-githubaction] 6 | [![Total downloads][downloads-badge]][link-packagist] 7 | [![Total stars][stars-badge]][link-github] 8 | 9 | [![Read my blog][blog-link-badge]][link-blog] 10 | [![View my other packages and projects][packages-link-badge]][link-packages] 11 | [![Follow @sebastiaanluca on Twitter][twitter-profile-badge]][link-twitter] 12 | [![Share this package on Twitter][twitter-share-badge]][link-twitter-share] 13 | 14 | ## Table of contents 15 | 16 | - [Requirements](#requirements) 17 | - [How to install](#how-to-install) 18 | - [Global helper functions](#global-helper-functions) 19 | - [rand_bool](#rand_bool) 20 | - [str_wrap](#str_wrap) 21 | - [is\_assoc\_array](#is_assoc_array) 22 | - [array_expand](#array_expand) 23 | - [array_without](#array_without) 24 | - [array\_pull\_value](#array_pull_value) 25 | - [array\_pull\_values](#array_pull_values) 26 | - [array_hash](#array_hash) 27 | - [object_hash](#object_hash) 28 | - [has\_public\_method](#has_public_method) 29 | - [carbon](#carbon) 30 | - [temporary_file](#temporary_file) 31 | - [Class helpers](#class-helpers) 32 | - [Constants trait](#constants-trait) 33 | - [Retrieving constants](#retrieving-constants) 34 | - [Retrieving constant keys](#retrieving-constant-keys) 35 | - [Retrieving constant values](#retrieving-constant-values) 36 | - [ProvidesClassInfo trait](#providesclassinfo-trait) 37 | - [MethodHelper](#methodhelper) 38 | - [License](#license) 39 | - [Change log](#change-log) 40 | - [Testing](#testing) 41 | - [Contributing](#contributing) 42 | - [Security](#security) 43 | - [Credits](#credits) 44 | - [About](#about) 45 | 46 | ## Requirements 47 | 48 | - PHP 8 or higher 49 | 50 | ## How to install 51 | 52 | Via Composer: 53 | 54 | ```bash 55 | composer require sebastiaanluca/php-helpers 56 | ``` 57 | 58 | All function helpers will be enabled by default (if those functions haven't already been defined). Class helpers are enabled per-case when used. 59 | 60 | You can find more info on how to use a helper and what there requirements are in their respective section (see the table of contents above for an overview). 61 | 62 | ## Global helper functions 63 | 64 | ### rand_bool 65 | 66 | Randomly return `true` or `false`. 67 | 68 | ```php 69 | rand_bool(); 70 | 71 | // true 72 | ``` 73 | 74 | ### str_wrap 75 | 76 | Wrap a string with another string. 77 | 78 | ```php 79 | str_wrap('foo', '*'); 80 | 81 | // "*foo*" 82 | ``` 83 | 84 | ### is\_assoc\_array 85 | 86 | Check if an array is associative. 87 | 88 | Performs a simple check to determine if the given array's keys are numeric, start at 0, and count up to the amount of values it has. 89 | 90 | ```php 91 | is_assoc_array(['color' => 'blue', 'age' => 31]); 92 | 93 | // true 94 | ``` 95 | 96 | ```php 97 | is_assoc_array([0 => 'blue', 7 => 31]); 98 | 99 | // true 100 | ``` 101 | 102 | ```php 103 | is_assoc_array(['blue', 31]); 104 | 105 | // false 106 | ``` 107 | 108 | ```php 109 | is_assoc_array([0 => 'blue', 1 => 31]); 110 | 111 | // false 112 | ``` 113 | 114 | ### array_expand 115 | 116 | Expand a flat dotted array into a multi-dimensional associative array. 117 | 118 | If a key is encountered that is already present and the existing value is an array, each new value will be added to that array. If it's not an array, each new value will override the existing one. 119 | 120 | ```php 121 | array_expand(['products.desk.price' => 200]); 122 | 123 | /* 124 | [ 125 | "products" => [ 126 | "desk" => [ 127 | "price" => 200, 128 | ], 129 | ], 130 | ] 131 | */ 132 | ``` 133 | 134 | ### array_without 135 | 136 | Get the array without the given values. 137 | 138 | Accepts either an array or a value as parameter to remove. 139 | 140 | ```php 141 | $cars = ['bmw', 'mercedes', 'audi']; 142 | $soldOut = ['audi', 'bmw']; 143 | 144 | $inStock = array_without($cars, $soldOut); 145 | 146 | // ["mercedes"] 147 | ``` 148 | 149 | ```php 150 | array_without(['one', 'two', 'three'], 'two'); 151 | 152 | // ["one", "three"] 153 | ``` 154 | 155 | ### array\_pull\_value 156 | 157 | Pull a single value from a given array. 158 | 159 | Returns the given value if it was successfully removed from the source array or `null` if it was not found. 160 | 161 | ```php 162 | $source = ['A', 'B', 'C']; 163 | 164 | $removed = array_pull_value($source, 'C'); 165 | 166 | // $removed = "C" 167 | // $source = ["A", "B"] 168 | ``` 169 | 170 | ### array\_pull\_values 171 | 172 | Pull an array of values from a given array. 173 | 174 | Returns the values that were successfully removed from the source array or an empty array if none were found. 175 | 176 | ```php 177 | $source = ['A', 'B', 'C']; 178 | $removed = array_pull_values($source, ['A', 'B']); 179 | 180 | // $removed = ["A", "B"] 181 | // $source = ["C"] 182 | ``` 183 | 184 | ### array_hash 185 | 186 | Create a unique string identifier for an array. 187 | 188 | The identifier will be entirely unique for each combination of keys and values. 189 | 190 | ```php 191 | array_hash([1, 2, 3]); 192 | 193 | // "262bbc0aa0dc62a93e350f1f7df792b9" 194 | ``` 195 | 196 | ```php 197 | array_hash(['hash' => 'me']); 198 | 199 | // "f712e79b502bda09a970e2d4d47e3f88" 200 | ``` 201 | 202 | ### object_hash 203 | 204 | Create a unique string identifier for an object. 205 | 206 | Similar to [array_hash](#array_hash), this uses `serialize` to *stringify* all public properties first. The identifier will be entirely unique based on the object class, properties, and its values. 207 | 208 | ```php 209 | class ValueObject { 210 | public $property = 'randomvalue'; 211 | } 212 | 213 | object_hash(new ValueObject); 214 | 215 | // "f39eaea7a1cf45f5a0c813d71b5f2f57" 216 | ``` 217 | 218 | ### has\_public\_method 219 | 220 | Check if a class has a certain public method. 221 | 222 | ```php 223 | class Hitchhiker { 224 | public function answer() { 225 | return 42; 226 | } 227 | } 228 | 229 | has_public_method(Hitchhiker::class, 'answer'); 230 | 231 | // true 232 | 233 | has_public_method(new Hitchhiker, 'answer'); 234 | 235 | // true 236 | ``` 237 | 238 | ### carbon 239 | 240 | Create a Carbon datetime object from a string or return a new object referencing the current date and time. 241 | 242 | Requires the [nesbot/carbon](https://github.com/briannesbitt/Carbon) package. 243 | 244 | ```php 245 | carbon('2017-01-18 11:30'); 246 | 247 | /* 248 | Carbon\Carbon { 249 | "date": "2017-01-18 11:30:00.000000", 250 | "timezone_type": 3, 251 | "timezone": "UTC", 252 | } 253 | */ 254 | 255 | carbon(); 256 | 257 | /* 258 | Carbon\Carbon { 259 | "date": "2017-10-27 16:18:00.000000", 260 | "timezone_type": 3, 261 | "timezone": "UTC", 262 | } 263 | */ 264 | ``` 265 | 266 | ### temporary_file 267 | 268 | Create a temporary file. 269 | 270 | Returns an array with the file handle (resource) and the full path as string. 271 | 272 | The temporary file is readable and writeable by default. The file is automatically removed when closed (for example, by calling fclose() on the handle, or when there are no remaining references to the file handle), or when the script ends. 273 | 274 | See [](http://php.net/manual/en/function.tmpfile.php) for more information. 275 | 276 | ```php 277 | temporary_file(); 278 | 279 | /* 280 | [ 281 | "file" => stream resource { 282 | timed_out: false 283 | blocked: true 284 | eof: false 285 | wrapper_type: "plainfile" 286 | stream_type: "STDIO" 287 | mode: "r+b" 288 | unread_bytes: 0 289 | seekable: true 290 | uri: "/tmp/phpxm4bcZ" 291 | options: [] 292 | } 293 | "path" => "/tmp/phpxm4bcZ" 294 | ] 295 | */ 296 | ``` 297 | 298 | ## Class helpers 299 | 300 | ### Enum trait 301 | 302 | The primary use of the `Enum` trait is to enable you to store all cases of a specific type in a single class or value object and have it return those with a single call. 303 | 304 | This can be useful for instance when your database uses integers to store states, but you want to use descriptive strings throughout your code (i.e. enums). It also allows you to refactor these elements at any time without having to waste time searching your code for any raw values (and probably miss a few, introducing new bugs along the way). 305 | 306 | #### Retrieving elements 307 | 308 | Returns an array of element keys and their values. 309 | 310 | ```php 311 | enums(); 329 | 330 | /* 331 | [ 332 | "REGISTERED" => "registered", 333 | "ACTIVATED" => "activated", 334 | "DISABLED" => "disabled", 335 | ] 336 | */ 337 | ``` 338 | 339 | #### Retrieving element keys 340 | 341 | Returns all the keys of the elements in an enum. 342 | 343 | ```php 344 | getClassDirectory()); 415 | } 416 | } 417 | 418 | // "/Users/Kyle/Projects/php-helpers" 419 | ``` 420 | 421 | ### MethodHelper 422 | 423 | A static class helper to help you figure out the visibility/accessibility of an object's methods. 424 | 425 | ```php 426 | 2 | 21 | 22 | 23 | src/ 24 | 25 | 26 | 27 | 28 | ./tests/Unit 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Classes/Enum.php: -------------------------------------------------------------------------------- 1 | getConstants(); 17 | } 18 | 19 | /** 20 | * Get all the names of the elements. 21 | * 22 | * @return array 23 | */ 24 | public static function keys() : array 25 | { 26 | return array_keys(static::enums()); 27 | } 28 | 29 | /** 30 | * Get all the values of the elements. 31 | * 32 | * @return array 33 | */ 34 | public static function values() : array 35 | { 36 | return array_values(static::enums()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Classes/MethodHelper.php: -------------------------------------------------------------------------------- 1 | {$type}(); 32 | } catch (ReflectionException $exception) { 33 | return false; 34 | } catch (Error $exception) { 35 | return false; 36 | } 37 | } 38 | 39 | /** 40 | * Check if an object has a given protected method. 41 | * 42 | * @param object $object 43 | * @param string $method 44 | * 45 | * @return bool 46 | */ 47 | public static function hasProtectedMethod($object, $method) : bool 48 | { 49 | return static::hasMethodOfType($object, $method, 'protected'); 50 | } 51 | 52 | /** 53 | * Check if an object has a given public method. 54 | * 55 | * @param object $object 56 | * @param string $method 57 | * 58 | * @return bool 59 | */ 60 | public static function hasPublicMethod($object, $method) : bool 61 | { 62 | return static::hasMethodOfType($object, $method, 'public'); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Classes/ProvidesClassInfo.php: -------------------------------------------------------------------------------- 1 | classDirectory) { 22 | return $this->classDirectory; 23 | } 24 | 25 | $reflection = new ReflectionClass(get_class($this)); 26 | 27 | $this->classDirectory = dirname($reflection->getFileName()); 28 | 29 | return $this->classDirectory; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Functions/generic.php: -------------------------------------------------------------------------------- 1 | $value) { 68 | InternalHelpers::arraySet($expanded, $key, $value); 69 | } 70 | 71 | return $expanded; 72 | } 73 | } 74 | 75 | if (! function_exists('array_without')) { 76 | /** 77 | * Get the array without the given values. 78 | * 79 | * @param array $array 80 | * @param array|string $values 81 | * 82 | * @return array 83 | */ 84 | function array_without(array $array, $values): array 85 | { 86 | $values = ! is_array($values) ? [$values] : $values; 87 | 88 | return array_values(array_diff($array, $values)); 89 | } 90 | } 91 | 92 | if (! function_exists('array_pull_values')) { 93 | /** 94 | * Pull an array of values from a given array. 95 | * 96 | * Returns the found values that were removed from the source array. 97 | * 98 | * @param array $array 99 | * @param array $values 100 | * 101 | * @return array 102 | */ 103 | function array_pull_values(array &$array, array $values): array 104 | { 105 | $matches = array_values(array_intersect($array, $values)); 106 | 107 | $array = array_without($array, $values); 108 | 109 | return $matches; 110 | } 111 | } 112 | 113 | if (! function_exists('array_pull_value')) { 114 | /** 115 | * Pull a value from a given array. 116 | * 117 | * Returns the given value if it was successfully removed from the source array. 118 | * 119 | * @param array $array 120 | * @param mixed $value 121 | * 122 | * @return mixed 123 | */ 124 | function array_pull_value(array &$array, $value) 125 | { 126 | $value = array_pull_values($array, [$value]); 127 | 128 | return array_shift($value); 129 | } 130 | } 131 | 132 | if (! function_exists('array_hash')) { 133 | /** 134 | * Create a unique identifier for a given array. 135 | * 136 | * @param array $array 137 | * 138 | * @return string 139 | */ 140 | function array_hash(array $array): string 141 | { 142 | return md5(serialize($array)); 143 | } 144 | } 145 | 146 | if (! function_exists('object_hash')) { 147 | /** 148 | * Create a unique identifier for a given object. 149 | * 150 | * @param $object 151 | * 152 | * @return string 153 | */ 154 | function object_hash($object): string 155 | { 156 | return md5(serialize($object)); 157 | } 158 | } 159 | 160 | if (! function_exists('has_public_method')) { 161 | /** 162 | * Check if a class has a certain public method. 163 | * 164 | * @param object $object 165 | * @param string $method 166 | * 167 | * @return bool 168 | */ 169 | function has_public_method($object, $method): bool 170 | { 171 | return MethodHelper::hasPublicMethod($object, $method); 172 | } 173 | } 174 | 175 | if (! function_exists('carbon')) { 176 | /** 177 | * Create a Carbon object from a string. 178 | * 179 | * @param string $timeString 180 | * 181 | * @return \Carbon\Carbon 182 | */ 183 | function carbon($timeString = null): Carbon 184 | { 185 | return new \Carbon\Carbon($timeString); 186 | } 187 | } 188 | 189 | if (! function_exists('temporary_file')) { 190 | /** 191 | * Create a temporary file. 192 | * 193 | * Returns an array with the file handle (resource) and the full path as string. 194 | * 195 | * The temporary file is readable and writeable by default. The file is automatically removed when 196 | * closed (for example, by calling fclose() on the handle, or when there are no remaining references 197 | * to the file handle), or when the script ends. 198 | * 199 | * @return array An array with a `file` and `path` key. 200 | */ 201 | function temporary_file(): array 202 | { 203 | $file = tmpfile(); 204 | $path = stream_get_meta_data($file)['uri']; 205 | 206 | return compact('file', 'path'); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/InternalHelpers.php: -------------------------------------------------------------------------------- 1 | 1) { 25 | $key = array_shift($keys); 26 | 27 | // If the key doesn't exist at this depth, we will just create an empty array 28 | // to hold the next value, allowing us to create the arrays to hold final 29 | // values at the correct depth. Then we'll keep digging into the array. 30 | if (! isset($array[$key]) || ! is_array($array[$key])) { 31 | $array[$key] = []; 32 | } 33 | 34 | $array = &$array[$key]; 35 | } 36 | 37 | $array[array_shift($keys)] = $value; 38 | 39 | return $array; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 1, 25 | 'SECOND_CONSTANT' => 2, 26 | 'THIRD_CONSTANT' => 3, 27 | ], $class::enums()); 28 | } 29 | 30 | /** 31 | * @test 32 | */ 33 | public function it returns all constant names(): void 34 | { 35 | $class = new class { 36 | use Enum; 37 | 38 | public const FIRST_CONSTANT = 1; 39 | public const SECOND_CONSTANT = 2; 40 | public const THIRD_CONSTANT = 'three'; 41 | }; 42 | 43 | static::assertEquals([ 44 | 'FIRST_CONSTANT', 45 | 'SECOND_CONSTANT', 46 | 'THIRD_CONSTANT', 47 | ], $class::keys()); 48 | } 49 | 50 | /** 51 | * @test 52 | */ 53 | public function it returns all constant values(): void 54 | { 55 | $class = new class { 56 | use Enum; 57 | 58 | public const FIRST_CONSTANT = 1; 59 | public const SECOND_CONSTANT = 'two'; 60 | public const THIRD_CONSTANT = 3; 61 | }; 62 | 63 | static::assertEquals([ 64 | 1, 65 | 'two', 66 | 3, 67 | ], $class::values()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/Unit/Classes/MethodHelperTest.php: -------------------------------------------------------------------------------- 1 | getClassDirectory(); 23 | } 24 | }; 25 | 26 | static::assertSame( 27 | dirname((new ReflectionClass($this))->getFileName()), 28 | $class->getDirectory() 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Functions/GenericHelpersTest.php: -------------------------------------------------------------------------------- 1 | 'value1', 33 | 'key2' => 'value2', 34 | ])); 35 | 36 | static::assertFalse(is_assoc_array(['value1', 'value2'])); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function array_expand expands a flat array(): void 43 | { 44 | static::assertSame([ 45 | 'a' => [ 46 | 'b' => [ 47 | 'c' => 1, 48 | ], 49 | 'd' => 2, 50 | ], 51 | 'e' => 3, 52 | ], array_expand([ 53 | 'a.b.c' => 1, 54 | 'a.d' => 2, 55 | 'e' => 3, 56 | ])); 57 | } 58 | 59 | /** 60 | * @test 61 | */ 62 | public function array_without removes values from an array(): void 63 | { 64 | static::assertSame(['a', 1], array_without(['a', 'b', 1], 'b')); 65 | static::assertSame(['a'], array_without(['a', 'b', 1], ['b', 1])); 66 | } 67 | 68 | /** 69 | * @test 70 | */ 71 | public function array_pull_values pulls values from an array(): void 72 | { 73 | $array = ['a', 'b', 'c']; 74 | 75 | static::assertSame(['b'], array_pull_values($array, ['b'])); 76 | static::assertSame(['a', 'c'], $array); 77 | } 78 | 79 | /** 80 | * @test 81 | */ 82 | public function array_pull_value pulls a value from an array(): void 83 | { 84 | $array = ['a', 'b', 'c']; 85 | 86 | static::assertSame('b', array_pull_value($array, 'b')); 87 | static::assertSame(['a', 'c'], $array); 88 | } 89 | 90 | /** 91 | * @test 92 | */ 93 | public function array_hash generates an array hash(): void 94 | { 95 | $hash = '9ae1f8db3c2cc8381e0811dda3316176'; 96 | 97 | static::assertSame($hash, array_hash(['value'])); 98 | static::assertNotSame($hash, array_hash(['value1', 'value2'])); 99 | } 100 | 101 | /** 102 | * @test 103 | */ 104 | public function object_hash generate an object hash(): void 105 | { 106 | $object = new \stdClass; 107 | $object->property = 'value'; 108 | 109 | $hash = '5439deb4526e33a32ffa80a485c623c4'; 110 | 111 | static::assertSame($hash, object_hash($object)); 112 | 113 | $object->property2 = 'value2'; 114 | 115 | static::assertNotSame($hash, object_hash($object)); 116 | } 117 | 118 | /** 119 | * @test 120 | */ 121 | public function carbon creates a carbon instance from a string(): void 122 | { 123 | static::assertEquals(new Carbon('tomorrow'), carbon('tomorrow')); 124 | } 125 | 126 | /** 127 | * @test 128 | */ 129 | public function has_public_method checks if a public method exists(): void 130 | { 131 | $class = new class { 132 | /** 133 | * @test 134 | */ 135 | public function myMethod() 136 | { 137 | return true; 138 | } 139 | }; 140 | 141 | static::assertTrue(has_public_method($class, 'myMethod')); 142 | static::assertFalse(has_public_method($class, 'myInvalidMethod')); 143 | } 144 | 145 | /** 146 | * @test 147 | */ 148 | public function temporary_file creates a temporary file and returns its pointer and full path(): void 149 | { 150 | $file = temporary_file(); 151 | 152 | static::assertArrayHasKey('file', $file); 153 | static::assertArrayHasKey('path', $file); 154 | 155 | static::assertIsResource($file['file']); 156 | 157 | static::assertIsString($file['path']); 158 | static::assertFileExists($file['path']); 159 | } 160 | 161 | /** 162 | * @test 163 | */ 164 | public function the file created by temporary_file is automatically deleted when it goes out of scope(): void 165 | { 166 | $file = temporary_file(); 167 | 168 | $path = $file['path']; 169 | 170 | unset($file); 171 | 172 | static::assertFileDoesNotExist($path); 173 | } 174 | } 175 | --------------------------------------------------------------------------------