├── .gitignore ├── stubs ├── res │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── values │ │ └── colors.xml │ ├── values-night │ │ └── themes.xml │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ └── drawable │ │ └── ic_launcher_background.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── gradlew.bat └── gradlew ├── tasks ├── stub.php └── generate_phar.php ├── composer.json ├── index.php ├── LICENSE.md ├── src ├── HelpersTrait.php ├── ChangableTrait.php └── GenerateCommand.php ├── README.md └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /tasks 3 | *.phar 4 | -------------------------------------------------------------------------------- /stubs/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnandPilania/php-android-cli/HEAD/stubs/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /stubs/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnandPilania/php-android-cli/HEAD/stubs/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /stubs/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnandPilania/php-android-cli/HEAD/stubs/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /stubs/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnandPilania/php-android-cli/HEAD/stubs/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /stubs/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnandPilania/php-android-cli/HEAD/stubs/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /stubs/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnandPilania/php-android-cli/HEAD/stubs/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tasks/stub.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /stubs/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | add(new \KSPEdu\PHPAndroidCli\GenerateCommand()); 12 | $app->run(); 13 | -------------------------------------------------------------------------------- /stubs/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /stubs/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /tasks/generate_phar.php: -------------------------------------------------------------------------------- 1 | hasChildren() && !in_array($file->getFilename(), $exclude)) { 20 | return true; 21 | } 22 | return $file->isFile() && !in_array($file->getFilename(), $exclude); 23 | }; 24 | 25 | $innerIterator = new RecursiveDirectoryIterator(BASEDIR, RecursiveDirectoryIterator::SKIP_DOTS); 26 | 27 | $iterator = new RecursiveIteratorIterator(new RecursiveCallbackFilterIterator($innerIterator, $filter)); 28 | 29 | $phar = new \Phar('phpandroid.phar', 0, 'phpandroid.phar'); 30 | $phar->setSignatureAlgorithm(\Phar::SHA1); 31 | $phar->startBuffering(); 32 | $phar->buildFromIterator($iterator, BASEDIR); 33 | $phar->setStub(file_get_contents(BUILDDIR . '/stub.php')); 34 | $phar->stopBuffering(); -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Anand Pilania 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stubs/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 16 | 19 | 22 | 23 | 24 | 25 | 31 | -------------------------------------------------------------------------------- /stubs/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/HelpersTrait.php: -------------------------------------------------------------------------------- 1 | deleteExisting($target . GenerateCommand::DS . $obj); 15 | } else { 16 | unlink($target . GenerateCommand::DS . $obj); 17 | } 18 | } 19 | } 20 | rmdir($target); 21 | } 22 | } 23 | protected function copyr($src, $dest) 24 | { 25 | if (is_link($src)) { 26 | return symlink(readlink($src), $dest); 27 | } 28 | 29 | if (is_file($src)) { 30 | return copy($src, $dest); 31 | } 32 | 33 | $this->_mkdir($dest); 34 | 35 | $dir = dir($src); 36 | while (false !== $entry = $dir->read()) { 37 | if ($entry == '.' || $entry == '..') { 38 | continue; 39 | } 40 | 41 | $this->copyr($src . GenerateCommand::DS . $entry, $dest . GenerateCommand::DS . $entry); 42 | } 43 | 44 | $dir->close(); 45 | return true; 46 | } 47 | protected function str_contains($haystack, $needles) 48 | { 49 | foreach ((array) $needles as $needle) { 50 | if ($needle != '' && mb_strpos($haystack, $needle) !== false) { 51 | return true; 52 | } 53 | } 54 | return false; 55 | } 56 | protected function _mkdir($dest) 57 | { 58 | if (!file_exists($dest) || !is_dir($dest)) { 59 | //$this->_print_info("$dest\n"); 60 | mkdir($dest); 61 | } 62 | } 63 | 64 | protected function _print_info($message) 65 | { 66 | $this->output->writeln('' . $message . ''); 67 | } 68 | 69 | protected function isKotlin() 70 | { 71 | return strtoupper($this->getInputOption('type')) === 'KOTLIN'; 72 | } 73 | 74 | protected function isLegacy() 75 | { 76 | return $this->getInputOption('legacy'); 77 | } 78 | 79 | protected function getInputOption($option) 80 | { 81 | return $this->input->getOption($option); 82 | } 83 | 84 | protected function nameForAsset($moduleName) 85 | { 86 | return ucfirst(strtolower($moduleName) === 'app' ? $this->projectName : $moduleName); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Android CLI 2 | 3 | [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FAnandPilania%2Fphp-android-cli&count_bg=%23FF3863&title_bg=%232C3E50&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) 4 | 5 | PHP Android CLI create/generate Scaffold Android-Studio Gradle project with: 6 | 7 | - `Java` or `Kotlin` language, 8 | - `legacy` or standard package 9 | - Modules (application/library) 10 | - Variants with Dimensions 11 | - Scaffold project & application/library level `build.gradle` with package name & `dimensions` & `variants` 12 | - Manage `settings.gradle` 13 | - Generate `manifest` file & `res` with default `icon`, `color`, `style` & `values` 14 | - ... 15 | 16 | # New Features! 17 | 18 | - Language select `java` or `kotlin` 19 | - Version `legacy` or standard 20 | 21 | # Removed! 22 | 23 | - `androidX` selection, 24 | - `jetifier` selection, 25 | 26 | You can also: 27 | 28 | - set `targetSdk` 29 | - set `buildToolsVersion` 30 | - set `minSdk` & `maxSdk` 31 | - `JAVA` or `KOTLIN` 32 | - `legacy` 33 | 34 | ### Tech 35 | 36 | PHP Android CLI uses: 37 | 38 | - [Symfony Console](https://symfony.com/console) - ... 39 | 40 | And of course `PHP Android CLI` itself is open source with a [public repository](https://github.com/AnandPilania/php-android-cli) on GitHub. 41 | 42 | ### Installation 43 | 44 | PHP Android CLI requires [PHP](https://php.net/) v5+ to run. 45 | 46 | Just download the [`phpandroid`](https://github.com/AnandPilania/php-android-cli/releases/latest) and start scaffolding. 47 | 48 | ```sh 49 | $ phpandroid create [OPTIONS] 50 | ``` 51 | 52 | ## USAGE 53 | 54 | ### Basic use 55 | 56 | Create `HelloWorld` project with `com.example.helloworld` package name: 57 | 58 | ```sh 59 | phpandroid create HelloWorld com.example.helloworld 60 | ``` 61 | 62 | ### Create `Modules` along with `App` 63 | 64 | Create `HelloWorld` project with `sdk` library & `admin` application 65 | 66 | ```sh 67 | phpandroid create HelloWorld com.example.helloworld --modules=sdk:library,admin 68 | ``` 69 | 70 | ### Create `productVariants`: `free` & `paid` variant 71 | 72 | ```sh 73 | phpandroid create HelloWorld com.example.helloworld --variants=free:type,paid:type 74 | ``` 75 | 76 | here `type` is the `dimension` 77 | 78 | ## Options 79 | 80 | ### Default 81 | 82 | PHP Android CLI is currently using default values for latest Android. These are: 83 | 84 | | OPTIONS | Usage | DEFAULT | 85 | | -------------------- | ----------------------- | -------- | 86 | | `--type`/`-t` | set `type` | `kotlin` | 87 | | `--legacy`/`-l` | set `legacy` | `false` | 88 | | `--compileSdk`/`-cs` | set `targetSdk` | 31 | 89 | | `--buildTools`/`-bt` | set `buildToolsVersion` | 31.0.0 | 90 | | `--minSdk`/`-ms` | set `minSdk` | 21 | 91 | | `--targetSdk`/`-ts` | set `maxSdk` | 31 | 92 | 93 | Use `--force` to re-write existing project. 94 | 95 | ### Todos 96 | 97 | - Create/Scaffold `activity` 98 | - Create/Scaffold `variants` source 99 | - ... 100 | 101 | ## License 102 | 103 | MIT 104 | 105 | **Free Software, Hell Yeah!** 106 | -------------------------------------------------------------------------------- /stubs/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /stubs/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/ChangableTrait.php: -------------------------------------------------------------------------------- 1 | getInputOption('compileSdk') . ' 51 | buildToolsVersion "' . $this->getInputOption('buildTools') . '" 52 | 53 | defaultConfig { 54 | applicationId "' . $this->pkgName . '" 55 | minSdkVersion ' . $this->getInputOption('minSdk') . ' 56 | targetSdkVersion ' . $this->getInputOption('targetSdk') . ' 57 | versionCode 1 58 | versionName "1.0" 59 | 60 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 61 | } 62 | 63 | buildTypes { 64 | release { 65 | minifyEnabled false 66 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 67 | } 68 | } 69 | compileOptions { 70 | sourceCompatibility JavaVersion.VERSION_1_8 71 | targetCompatibility JavaVersion.VERSION_1_8 72 | } 73 | ' . ($isKotlin ? 'kotlinOptions { 74 | jvmTarget = "1.8" 75 | }' : '') . ' 76 | } 77 | 78 | dependencies { 79 | 80 | ' . ($isKotlin ? 'implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"' : '') . ' 81 | implementation "com.android.support:appcompat-v7:28.0.0" 82 | testImplementation "junit:junit:4.+" 83 | androidTestImplementation "com.android.support.test:runner:1.0.2" 84 | androidTestImplementation "com.android.support.test.espresso:espresso-core:3.0.2" 85 | } 86 | '; 87 | } 88 | 89 | protected function gradleProperties($isKotlin) 90 | { 91 | return " 92 | # Project-wide Gradle settings. 93 | # IDE (e.g. Android Studio) users: 94 | # Gradle settings configured through the IDE *will override* 95 | # any settings specified in this file. 96 | # For more details on how to configure your build environment visit 97 | # http://www.gradle.org/docs/current/userguide/build_environment.html 98 | # Specifies the JVM arguments used for the daemon process. 99 | # The setting is particularly useful for tweaking memory settings. 100 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 101 | # When configured, Gradle will run in incubating parallel mode. 102 | # This option should only be used with decoupled projects. More details, visit 103 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 104 | # org.gradle.parallel=true 105 | # Kotlin code style for this project: \"official\" or \"obsolete\": 106 | " . ($isKotlin ? "kotlin.code.style=official" : '') . " 107 | "; 108 | } 109 | 110 | protected function manifestApplicationFile($moduleName) 111 | { 112 | return ' 113 | 114 | 116 | 117 | 124 | 125 | 126 | '; 127 | } 128 | 129 | // TODO: 130 | protected function manifestModuleFile($moduleName, $moduleType) 131 | { 132 | return ' 133 | 135 | 136 | ' . ($moduleType === 'library' ? '' : ' 143 | ') . ' 144 | 145 | '; 146 | } 147 | 148 | protected function proguardFile() 149 | { 150 | return << 179 | ' . ('app' === $moduleName ? $this->projectName : $moduleName) . ' 180 | 181 | '; 182 | } 183 | 184 | protected function themesFile($moduleName, $isDark = false) 185 | { 186 | return ' 187 | 188 | 189 | 202 | 203 | '; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/GenerateCommand.php: -------------------------------------------------------------------------------- 1 | setName('create') 24 | ->setDescription('Create android studio gradle project skeleton') 25 | ->addArgument('project', InputArgument::REQUIRED, 'Provide the project name') 26 | ->addArgument('pkg', InputArgument::REQUIRED, 'Provide the pkg name') 27 | ->addOption('targetSdk', 'ts', InputOption::VALUE_OPTIONAL, 'Pass the targetSdk.', 31) 28 | ->addOption('minSdk', 'ms', InputOption::VALUE_OPTIONAL, 'Pass the minSdk.', 21) 29 | ->addOption('compileSdk', 'cs', InputOption::VALUE_OPTIONAL, 'Pass the compileSdk.', 31) 30 | ->addOption('buildTools', 'bt', InputOption::VALUE_OPTIONAL, 'Pass the buildVersion.', '31.0.0') 31 | ->addOption('type', 't', InputOption::VALUE_OPTIONAL, 'JAVA or KOTLIN', 'kotlin') 32 | ->addOption('legacy', 'l', InputOption::VALUE_OPTIONAL, 'Using Legacy deps?', false) 33 | ->addOption('modules', 'm', InputOption::VALUE_OPTIONAL, 'Comma-seperated modules [with semi-colon seperated type] (backend:library,client)') 34 | ->addOption('flavors', 'f', InputOption::VALUE_OPTIONAL, 'Comma-seperated flavors followed by dimension with semi-colon (free:type,paid:type,php:backend,firebase:backend)') 35 | ->addOption('force', null) 36 | ->setHelp( 37 | <<%command.name% command helps you generates new Android-Studio Gradle Project. 39 | 40 | By default, the command interacts with the developer to tweak the generation. 41 | Any passed option will be used as a default value for the interaction 42 | (project & pkg is the only two needed if you follow the conventions): 43 | 44 | php %command.full_name% HelloWorld com.example.helloworld 45 | 46 | If you want to disable any user interaction, use --no-interaction 47 | EOT 48 | ); 49 | } 50 | 51 | protected function execute(InputInterface $input, OutputInterface $output) 52 | { 53 | $this->input = $input; 54 | $this->output = $output; 55 | 56 | $this->projectName = $input->getArgument('project'); 57 | $this->pkgName = $input->getArgument('pkg'); 58 | 59 | $this->generateOptions(); 60 | $this->createProject(); 61 | 62 | return 0; 63 | } 64 | 65 | private function createProject() 66 | { 67 | if (is_dir($this->projectName)) { 68 | if (!$this->input->getOption('force')) { 69 | $this->output->writeln('Project ' . $this->projectName . ' is already exists!'); 70 | $this->output->writeln('Use: --force/-ff for forcefully delete an existing project!'); 71 | exit(); 72 | } else { 73 | $this->deleteExisting($this->projectName); 74 | } 75 | } 76 | 77 | $this->_mkdir($this->projectName); 78 | $this->copyr($this->stubsPath . 'gradle', $this->projectName . self::DS . 'gradle'); 79 | copy($this->stubsPath . 'gradlew', $this->projectName . self::DS . 'gradlew'); 80 | copy($this->stubsPath . 'gradlew.bat', $this->projectName . self::DS . 'gradlew.bat'); 81 | copy($this->stubsPath . '.gitignore', $this->projectName . self::DS . '.gitignore'); 82 | $this->_mkdir($this->projectName . self::DS . 'build'); 83 | 84 | file_put_contents($this->projectName . self::DS . 'build.gradle', $this->projectBuildGradle($this->isKotlin()), 0); 85 | file_put_contents($this->projectName . self::DS . 'gradle.properties', $this->gradleProperties($this->isKotlin()), 0); 86 | 87 | $settingsContent = "include "; 88 | 89 | foreach ($this->modules as $key => $module) { 90 | $exModule = explode(':', $module); 91 | if ($exModule > 0) { 92 | $moduleName = $exModule[0]; 93 | $moduleType = isset($exModule[1]) ? $exModule[1] : 'application'; 94 | } 95 | 96 | $this->_mkdir($this->projectName . self::DS . $moduleName); 97 | 98 | file_put_contents($this->projectName . self::DS . $moduleName . self::DS . '.gitignore', "/build\n\n", 0); 99 | 100 | $fdCount = 0; 101 | $flavorContent = ''; 102 | $dimensionContent = ''; 103 | if ($count = count($this->flavors)) { 104 | $flavorContent = "productFlavors {\n\t\t"; 105 | $dimensionContent = 'flavorDimensions '; 106 | foreach ($this->flavors as $flavor => $dimension) { 107 | $dimenPushed = $this->str_contains($dimensionContent, "'$dimension'"); 108 | if (!$dimenPushed) { 109 | $dimensionContent .= "'$dimension'"; 110 | } 111 | $flavorContent .= $flavor . " {\n\t\t\tdimension '$dimension'\n\t\t}"; 112 | 113 | if ($fdCount < ($count - 1)) { 114 | $fdCount = $fdCount + 1; 115 | if (!$dimenPushed) { 116 | $dimensionContent .= ", "; 117 | } 118 | $flavorContent .= "\n\t\t"; 119 | } 120 | } 121 | $flavorContent .= "\n\t}\n"; 122 | } 123 | $fdContent = $dimensionContent . "\n\t" . $flavorContent; 124 | 125 | file_put_contents( 126 | $this->projectName . self::DS . $moduleName . self::DS . 'build.gradle', 127 | $this->appBuildGradle($this->isKotlin()), 128 | 0 129 | ); 130 | file_put_contents($this->projectName . self::DS . $moduleName . self::DS . 'proguard-rules.pro', $this->proguardFile(), 0); 131 | 132 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src'); 133 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'build'); 134 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'libs'); 135 | 136 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src/androidTest'); 137 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src/test'); 138 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src/main'); 139 | 140 | file_put_contents( 141 | $this->projectName . self::DS . $moduleName . self::DS . 'src/main/AndroidManifest.xml', 142 | $moduleType === 'application' ? $this->manifestApplicationFile($this->nameForAsset($moduleName)) : $this->manifestModuleFile($moduleName, $moduleType), 143 | 0 144 | ); 145 | 146 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src/main/java'); 147 | $exPkg = explode('.', $this->pkgName); 148 | $_dir = $this->projectName . self::DS . $moduleName . self::DS . 'src/main/java'; 149 | $_newDir = ''; 150 | foreach ($exPkg as $key => $pkgDir) { 151 | $_newDir .= self::DS . $pkgDir; 152 | $this->_mkdir($_dir . $_newDir); 153 | if ($key === (count($exPkg) - 1) && $moduleName !== 'app') { 154 | $this->_mkdir($_dir . $_newDir . self::DS . $moduleName); 155 | } 156 | } 157 | 158 | if ($moduleType === 'library') { 159 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src' . self::DS . 'main' . self::DS . 'res'); 160 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src' . self::DS . 'main' . self::DS . 'res' . self::DS . 'drawable'); 161 | $this->_mkdir($this->projectName . self::DS . $moduleName . self::DS . 'src' . self::DS . 'main' . self::DS . 'res' . self::DS . 'values'); 162 | } else { 163 | $this->copyr($this->stubsPath . 'res', $this->projectName . self::DS . $moduleName . self::DS . 'src' . self::DS . 'main' . self::DS . 'res'); 164 | } 165 | file_put_contents($this->projectName . self::DS . $moduleName . self::DS . 'src/main/res/values/strings.xml', $this->stringsFile($this->nameForAsset($moduleName)), 0); 166 | file_put_contents($this->projectName . self::DS . $moduleName . self::DS . 'src/main/res/values/themes.xml', $this->themesFile($this->nameForAsset($moduleName)), 0); 167 | file_put_contents($this->projectName . self::DS . $moduleName . self::DS . 'src/main/res/values-night/themes.xml', $this->themesFile($this->nameForAsset($moduleName), true), 0); 168 | 169 | $settingsContent .= "':" . $moduleName . "'"; 170 | 171 | if ($key < (count($this->modules) - 1)) { 172 | $settingsContent .= ","; 173 | } 174 | } 175 | file_put_contents($this->projectName . self::DS . 'settings.gradle', $settingsContent . "\n", 0); 176 | // file_put_contents($this->projectName . DIRECTORY_SEPARATOR . 'local.properties', "ndk.dir=E\:\\SDK\\ndk-bundle\nsdk.dir=E\:\\SDK\n", 0); 177 | 178 | $this->output->writeln('' . $this->projectName . ' created successfully!'); 179 | } 180 | 181 | private function generateOptions() 182 | { 183 | $this->generateFlavors($this->input->getOption('flavors')); 184 | $this->generateModules($this->input->getOption('modules')); 185 | } 186 | 187 | private function generateFlavors($flavors = null) 188 | { 189 | if ($flavors) { 190 | $flavors = explode(',', $flavors); 191 | if (is_array($flavors) && count($flavors)) { 192 | foreach ($flavors as $flavor) { 193 | $exFlavor = explode(':', $flavor); 194 | if (!isset($exFlavor[1])) { 195 | $this->output->writeln('Flavors must-be followed by specific dimension'); 196 | exit(); 197 | } 198 | 199 | $this->flavors[$exFlavor[0]] = $exFlavor[1]; 200 | } 201 | } 202 | } 203 | } 204 | private function generateModules($modules = null) 205 | { 206 | if ($modules) { 207 | $modules = explode(',', $modules); 208 | if (is_array($modules) && count($modules)) { 209 | $_mergedModules = array_merge($this->modules, $modules); 210 | $this->modules = $_mergedModules; 211 | } 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "ccda0f1fbd5b4f64d57743fa6288ecac", 8 | "packages": [ 9 | { 10 | "name": "psr/container", 11 | "version": "1.1.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/php-fig/container.git", 15 | "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", 20 | "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=7.2.0" 25 | }, 26 | "type": "library", 27 | "autoload": { 28 | "psr-4": { 29 | "Psr\\Container\\": "src/" 30 | } 31 | }, 32 | "notification-url": "https://packagist.org/downloads/", 33 | "license": [ 34 | "MIT" 35 | ], 36 | "authors": [ 37 | { 38 | "name": "PHP-FIG", 39 | "homepage": "https://www.php-fig.org/" 40 | } 41 | ], 42 | "description": "Common Container Interface (PHP FIG PSR-11)", 43 | "homepage": "https://github.com/php-fig/container", 44 | "keywords": [ 45 | "PSR-11", 46 | "container", 47 | "container-interface", 48 | "container-interop", 49 | "psr" 50 | ], 51 | "support": { 52 | "issues": "https://github.com/php-fig/container/issues", 53 | "source": "https://github.com/php-fig/container/tree/1.1.1" 54 | }, 55 | "time": "2021-03-05T17:36:06+00:00" 56 | }, 57 | { 58 | "name": "symfony/console", 59 | "version": "v5.3.7", 60 | "source": { 61 | "type": "git", 62 | "url": "https://github.com/symfony/console.git", 63 | "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" 64 | }, 65 | "dist": { 66 | "type": "zip", 67 | "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", 68 | "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", 69 | "shasum": "" 70 | }, 71 | "require": { 72 | "php": ">=7.2.5", 73 | "symfony/deprecation-contracts": "^2.1", 74 | "symfony/polyfill-mbstring": "~1.0", 75 | "symfony/polyfill-php73": "^1.8", 76 | "symfony/polyfill-php80": "^1.16", 77 | "symfony/service-contracts": "^1.1|^2", 78 | "symfony/string": "^5.1" 79 | }, 80 | "conflict": { 81 | "psr/log": ">=3", 82 | "symfony/dependency-injection": "<4.4", 83 | "symfony/dotenv": "<5.1", 84 | "symfony/event-dispatcher": "<4.4", 85 | "symfony/lock": "<4.4", 86 | "symfony/process": "<4.4" 87 | }, 88 | "provide": { 89 | "psr/log-implementation": "1.0|2.0" 90 | }, 91 | "require-dev": { 92 | "psr/log": "^1|^2", 93 | "symfony/config": "^4.4|^5.0", 94 | "symfony/dependency-injection": "^4.4|^5.0", 95 | "symfony/event-dispatcher": "^4.4|^5.0", 96 | "symfony/lock": "^4.4|^5.0", 97 | "symfony/process": "^4.4|^5.0", 98 | "symfony/var-dumper": "^4.4|^5.0" 99 | }, 100 | "suggest": { 101 | "psr/log": "For using the console logger", 102 | "symfony/event-dispatcher": "", 103 | "symfony/lock": "", 104 | "symfony/process": "" 105 | }, 106 | "type": "library", 107 | "autoload": { 108 | "psr-4": { 109 | "Symfony\\Component\\Console\\": "" 110 | }, 111 | "exclude-from-classmap": [ 112 | "/Tests/" 113 | ] 114 | }, 115 | "notification-url": "https://packagist.org/downloads/", 116 | "license": [ 117 | "MIT" 118 | ], 119 | "authors": [ 120 | { 121 | "name": "Fabien Potencier", 122 | "email": "fabien@symfony.com" 123 | }, 124 | { 125 | "name": "Symfony Community", 126 | "homepage": "https://symfony.com/contributors" 127 | } 128 | ], 129 | "description": "Eases the creation of beautiful and testable command line interfaces", 130 | "homepage": "https://symfony.com", 131 | "keywords": [ 132 | "cli", 133 | "command line", 134 | "console", 135 | "terminal" 136 | ], 137 | "support": { 138 | "source": "https://github.com/symfony/console/tree/v5.3.7" 139 | }, 140 | "funding": [ 141 | { 142 | "url": "https://symfony.com/sponsor", 143 | "type": "custom" 144 | }, 145 | { 146 | "url": "https://github.com/fabpot", 147 | "type": "github" 148 | }, 149 | { 150 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 151 | "type": "tidelift" 152 | } 153 | ], 154 | "time": "2021-08-25T20:02:16+00:00" 155 | }, 156 | { 157 | "name": "symfony/deprecation-contracts", 158 | "version": "v2.4.0", 159 | "source": { 160 | "type": "git", 161 | "url": "https://github.com/symfony/deprecation-contracts.git", 162 | "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" 163 | }, 164 | "dist": { 165 | "type": "zip", 166 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", 167 | "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", 168 | "shasum": "" 169 | }, 170 | "require": { 171 | "php": ">=7.1" 172 | }, 173 | "type": "library", 174 | "extra": { 175 | "branch-alias": { 176 | "dev-main": "2.4-dev" 177 | }, 178 | "thanks": { 179 | "name": "symfony/contracts", 180 | "url": "https://github.com/symfony/contracts" 181 | } 182 | }, 183 | "autoload": { 184 | "files": [ 185 | "function.php" 186 | ] 187 | }, 188 | "notification-url": "https://packagist.org/downloads/", 189 | "license": [ 190 | "MIT" 191 | ], 192 | "authors": [ 193 | { 194 | "name": "Nicolas Grekas", 195 | "email": "p@tchwork.com" 196 | }, 197 | { 198 | "name": "Symfony Community", 199 | "homepage": "https://symfony.com/contributors" 200 | } 201 | ], 202 | "description": "A generic function and convention to trigger deprecation notices", 203 | "homepage": "https://symfony.com", 204 | "support": { 205 | "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" 206 | }, 207 | "funding": [ 208 | { 209 | "url": "https://symfony.com/sponsor", 210 | "type": "custom" 211 | }, 212 | { 213 | "url": "https://github.com/fabpot", 214 | "type": "github" 215 | }, 216 | { 217 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 218 | "type": "tidelift" 219 | } 220 | ], 221 | "time": "2021-03-23T23:28:01+00:00" 222 | }, 223 | { 224 | "name": "symfony/polyfill-ctype", 225 | "version": "v1.23.0", 226 | "source": { 227 | "type": "git", 228 | "url": "https://github.com/symfony/polyfill-ctype.git", 229 | "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" 230 | }, 231 | "dist": { 232 | "type": "zip", 233 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", 234 | "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", 235 | "shasum": "" 236 | }, 237 | "require": { 238 | "php": ">=7.1" 239 | }, 240 | "suggest": { 241 | "ext-ctype": "For best performance" 242 | }, 243 | "type": "library", 244 | "extra": { 245 | "branch-alias": { 246 | "dev-main": "1.23-dev" 247 | }, 248 | "thanks": { 249 | "name": "symfony/polyfill", 250 | "url": "https://github.com/symfony/polyfill" 251 | } 252 | }, 253 | "autoload": { 254 | "psr-4": { 255 | "Symfony\\Polyfill\\Ctype\\": "" 256 | }, 257 | "files": [ 258 | "bootstrap.php" 259 | ] 260 | }, 261 | "notification-url": "https://packagist.org/downloads/", 262 | "license": [ 263 | "MIT" 264 | ], 265 | "authors": [ 266 | { 267 | "name": "Gert de Pagter", 268 | "email": "BackEndTea@gmail.com" 269 | }, 270 | { 271 | "name": "Symfony Community", 272 | "homepage": "https://symfony.com/contributors" 273 | } 274 | ], 275 | "description": "Symfony polyfill for ctype functions", 276 | "homepage": "https://symfony.com", 277 | "keywords": [ 278 | "compatibility", 279 | "ctype", 280 | "polyfill", 281 | "portable" 282 | ], 283 | "support": { 284 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" 285 | }, 286 | "funding": [ 287 | { 288 | "url": "https://symfony.com/sponsor", 289 | "type": "custom" 290 | }, 291 | { 292 | "url": "https://github.com/fabpot", 293 | "type": "github" 294 | }, 295 | { 296 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 297 | "type": "tidelift" 298 | } 299 | ], 300 | "time": "2021-02-19T12:13:01+00:00" 301 | }, 302 | { 303 | "name": "symfony/polyfill-intl-grapheme", 304 | "version": "v1.23.1", 305 | "source": { 306 | "type": "git", 307 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 308 | "reference": "16880ba9c5ebe3642d1995ab866db29270b36535" 309 | }, 310 | "dist": { 311 | "type": "zip", 312 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", 313 | "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", 314 | "shasum": "" 315 | }, 316 | "require": { 317 | "php": ">=7.1" 318 | }, 319 | "suggest": { 320 | "ext-intl": "For best performance" 321 | }, 322 | "type": "library", 323 | "extra": { 324 | "branch-alias": { 325 | "dev-main": "1.23-dev" 326 | }, 327 | "thanks": { 328 | "name": "symfony/polyfill", 329 | "url": "https://github.com/symfony/polyfill" 330 | } 331 | }, 332 | "autoload": { 333 | "psr-4": { 334 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 335 | }, 336 | "files": [ 337 | "bootstrap.php" 338 | ] 339 | }, 340 | "notification-url": "https://packagist.org/downloads/", 341 | "license": [ 342 | "MIT" 343 | ], 344 | "authors": [ 345 | { 346 | "name": "Nicolas Grekas", 347 | "email": "p@tchwork.com" 348 | }, 349 | { 350 | "name": "Symfony Community", 351 | "homepage": "https://symfony.com/contributors" 352 | } 353 | ], 354 | "description": "Symfony polyfill for intl's grapheme_* functions", 355 | "homepage": "https://symfony.com", 356 | "keywords": [ 357 | "compatibility", 358 | "grapheme", 359 | "intl", 360 | "polyfill", 361 | "portable", 362 | "shim" 363 | ], 364 | "support": { 365 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1" 366 | }, 367 | "funding": [ 368 | { 369 | "url": "https://symfony.com/sponsor", 370 | "type": "custom" 371 | }, 372 | { 373 | "url": "https://github.com/fabpot", 374 | "type": "github" 375 | }, 376 | { 377 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 378 | "type": "tidelift" 379 | } 380 | ], 381 | "time": "2021-05-27T12:26:48+00:00" 382 | }, 383 | { 384 | "name": "symfony/polyfill-intl-normalizer", 385 | "version": "v1.23.0", 386 | "source": { 387 | "type": "git", 388 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 389 | "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" 390 | }, 391 | "dist": { 392 | "type": "zip", 393 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", 394 | "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", 395 | "shasum": "" 396 | }, 397 | "require": { 398 | "php": ">=7.1" 399 | }, 400 | "suggest": { 401 | "ext-intl": "For best performance" 402 | }, 403 | "type": "library", 404 | "extra": { 405 | "branch-alias": { 406 | "dev-main": "1.23-dev" 407 | }, 408 | "thanks": { 409 | "name": "symfony/polyfill", 410 | "url": "https://github.com/symfony/polyfill" 411 | } 412 | }, 413 | "autoload": { 414 | "psr-4": { 415 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 416 | }, 417 | "files": [ 418 | "bootstrap.php" 419 | ], 420 | "classmap": [ 421 | "Resources/stubs" 422 | ] 423 | }, 424 | "notification-url": "https://packagist.org/downloads/", 425 | "license": [ 426 | "MIT" 427 | ], 428 | "authors": [ 429 | { 430 | "name": "Nicolas Grekas", 431 | "email": "p@tchwork.com" 432 | }, 433 | { 434 | "name": "Symfony Community", 435 | "homepage": "https://symfony.com/contributors" 436 | } 437 | ], 438 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 439 | "homepage": "https://symfony.com", 440 | "keywords": [ 441 | "compatibility", 442 | "intl", 443 | "normalizer", 444 | "polyfill", 445 | "portable", 446 | "shim" 447 | ], 448 | "support": { 449 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" 450 | }, 451 | "funding": [ 452 | { 453 | "url": "https://symfony.com/sponsor", 454 | "type": "custom" 455 | }, 456 | { 457 | "url": "https://github.com/fabpot", 458 | "type": "github" 459 | }, 460 | { 461 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 462 | "type": "tidelift" 463 | } 464 | ], 465 | "time": "2021-02-19T12:13:01+00:00" 466 | }, 467 | { 468 | "name": "symfony/polyfill-mbstring", 469 | "version": "v1.23.1", 470 | "source": { 471 | "type": "git", 472 | "url": "https://github.com/symfony/polyfill-mbstring.git", 473 | "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" 474 | }, 475 | "dist": { 476 | "type": "zip", 477 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", 478 | "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", 479 | "shasum": "" 480 | }, 481 | "require": { 482 | "php": ">=7.1" 483 | }, 484 | "suggest": { 485 | "ext-mbstring": "For best performance" 486 | }, 487 | "type": "library", 488 | "extra": { 489 | "branch-alias": { 490 | "dev-main": "1.23-dev" 491 | }, 492 | "thanks": { 493 | "name": "symfony/polyfill", 494 | "url": "https://github.com/symfony/polyfill" 495 | } 496 | }, 497 | "autoload": { 498 | "psr-4": { 499 | "Symfony\\Polyfill\\Mbstring\\": "" 500 | }, 501 | "files": [ 502 | "bootstrap.php" 503 | ] 504 | }, 505 | "notification-url": "https://packagist.org/downloads/", 506 | "license": [ 507 | "MIT" 508 | ], 509 | "authors": [ 510 | { 511 | "name": "Nicolas Grekas", 512 | "email": "p@tchwork.com" 513 | }, 514 | { 515 | "name": "Symfony Community", 516 | "homepage": "https://symfony.com/contributors" 517 | } 518 | ], 519 | "description": "Symfony polyfill for the Mbstring extension", 520 | "homepage": "https://symfony.com", 521 | "keywords": [ 522 | "compatibility", 523 | "mbstring", 524 | "polyfill", 525 | "portable", 526 | "shim" 527 | ], 528 | "support": { 529 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" 530 | }, 531 | "funding": [ 532 | { 533 | "url": "https://symfony.com/sponsor", 534 | "type": "custom" 535 | }, 536 | { 537 | "url": "https://github.com/fabpot", 538 | "type": "github" 539 | }, 540 | { 541 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 542 | "type": "tidelift" 543 | } 544 | ], 545 | "time": "2021-05-27T12:26:48+00:00" 546 | }, 547 | { 548 | "name": "symfony/polyfill-php73", 549 | "version": "v1.23.0", 550 | "source": { 551 | "type": "git", 552 | "url": "https://github.com/symfony/polyfill-php73.git", 553 | "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" 554 | }, 555 | "dist": { 556 | "type": "zip", 557 | "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", 558 | "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", 559 | "shasum": "" 560 | }, 561 | "require": { 562 | "php": ">=7.1" 563 | }, 564 | "type": "library", 565 | "extra": { 566 | "branch-alias": { 567 | "dev-main": "1.23-dev" 568 | }, 569 | "thanks": { 570 | "name": "symfony/polyfill", 571 | "url": "https://github.com/symfony/polyfill" 572 | } 573 | }, 574 | "autoload": { 575 | "psr-4": { 576 | "Symfony\\Polyfill\\Php73\\": "" 577 | }, 578 | "files": [ 579 | "bootstrap.php" 580 | ], 581 | "classmap": [ 582 | "Resources/stubs" 583 | ] 584 | }, 585 | "notification-url": "https://packagist.org/downloads/", 586 | "license": [ 587 | "MIT" 588 | ], 589 | "authors": [ 590 | { 591 | "name": "Nicolas Grekas", 592 | "email": "p@tchwork.com" 593 | }, 594 | { 595 | "name": "Symfony Community", 596 | "homepage": "https://symfony.com/contributors" 597 | } 598 | ], 599 | "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 600 | "homepage": "https://symfony.com", 601 | "keywords": [ 602 | "compatibility", 603 | "polyfill", 604 | "portable", 605 | "shim" 606 | ], 607 | "support": { 608 | "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" 609 | }, 610 | "funding": [ 611 | { 612 | "url": "https://symfony.com/sponsor", 613 | "type": "custom" 614 | }, 615 | { 616 | "url": "https://github.com/fabpot", 617 | "type": "github" 618 | }, 619 | { 620 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 621 | "type": "tidelift" 622 | } 623 | ], 624 | "time": "2021-02-19T12:13:01+00:00" 625 | }, 626 | { 627 | "name": "symfony/polyfill-php80", 628 | "version": "v1.23.1", 629 | "source": { 630 | "type": "git", 631 | "url": "https://github.com/symfony/polyfill-php80.git", 632 | "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" 633 | }, 634 | "dist": { 635 | "type": "zip", 636 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", 637 | "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", 638 | "shasum": "" 639 | }, 640 | "require": { 641 | "php": ">=7.1" 642 | }, 643 | "type": "library", 644 | "extra": { 645 | "branch-alias": { 646 | "dev-main": "1.23-dev" 647 | }, 648 | "thanks": { 649 | "name": "symfony/polyfill", 650 | "url": "https://github.com/symfony/polyfill" 651 | } 652 | }, 653 | "autoload": { 654 | "psr-4": { 655 | "Symfony\\Polyfill\\Php80\\": "" 656 | }, 657 | "files": [ 658 | "bootstrap.php" 659 | ], 660 | "classmap": [ 661 | "Resources/stubs" 662 | ] 663 | }, 664 | "notification-url": "https://packagist.org/downloads/", 665 | "license": [ 666 | "MIT" 667 | ], 668 | "authors": [ 669 | { 670 | "name": "Ion Bazan", 671 | "email": "ion.bazan@gmail.com" 672 | }, 673 | { 674 | "name": "Nicolas Grekas", 675 | "email": "p@tchwork.com" 676 | }, 677 | { 678 | "name": "Symfony Community", 679 | "homepage": "https://symfony.com/contributors" 680 | } 681 | ], 682 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 683 | "homepage": "https://symfony.com", 684 | "keywords": [ 685 | "compatibility", 686 | "polyfill", 687 | "portable", 688 | "shim" 689 | ], 690 | "support": { 691 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" 692 | }, 693 | "funding": [ 694 | { 695 | "url": "https://symfony.com/sponsor", 696 | "type": "custom" 697 | }, 698 | { 699 | "url": "https://github.com/fabpot", 700 | "type": "github" 701 | }, 702 | { 703 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 704 | "type": "tidelift" 705 | } 706 | ], 707 | "time": "2021-07-28T13:41:28+00:00" 708 | }, 709 | { 710 | "name": "symfony/service-contracts", 711 | "version": "v2.4.0", 712 | "source": { 713 | "type": "git", 714 | "url": "https://github.com/symfony/service-contracts.git", 715 | "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" 716 | }, 717 | "dist": { 718 | "type": "zip", 719 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", 720 | "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", 721 | "shasum": "" 722 | }, 723 | "require": { 724 | "php": ">=7.2.5", 725 | "psr/container": "^1.1" 726 | }, 727 | "suggest": { 728 | "symfony/service-implementation": "" 729 | }, 730 | "type": "library", 731 | "extra": { 732 | "branch-alias": { 733 | "dev-main": "2.4-dev" 734 | }, 735 | "thanks": { 736 | "name": "symfony/contracts", 737 | "url": "https://github.com/symfony/contracts" 738 | } 739 | }, 740 | "autoload": { 741 | "psr-4": { 742 | "Symfony\\Contracts\\Service\\": "" 743 | } 744 | }, 745 | "notification-url": "https://packagist.org/downloads/", 746 | "license": [ 747 | "MIT" 748 | ], 749 | "authors": [ 750 | { 751 | "name": "Nicolas Grekas", 752 | "email": "p@tchwork.com" 753 | }, 754 | { 755 | "name": "Symfony Community", 756 | "homepage": "https://symfony.com/contributors" 757 | } 758 | ], 759 | "description": "Generic abstractions related to writing services", 760 | "homepage": "https://symfony.com", 761 | "keywords": [ 762 | "abstractions", 763 | "contracts", 764 | "decoupling", 765 | "interfaces", 766 | "interoperability", 767 | "standards" 768 | ], 769 | "support": { 770 | "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" 771 | }, 772 | "funding": [ 773 | { 774 | "url": "https://symfony.com/sponsor", 775 | "type": "custom" 776 | }, 777 | { 778 | "url": "https://github.com/fabpot", 779 | "type": "github" 780 | }, 781 | { 782 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 783 | "type": "tidelift" 784 | } 785 | ], 786 | "time": "2021-04-01T10:43:52+00:00" 787 | }, 788 | { 789 | "name": "symfony/string", 790 | "version": "v5.3.7", 791 | "source": { 792 | "type": "git", 793 | "url": "https://github.com/symfony/string.git", 794 | "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" 795 | }, 796 | "dist": { 797 | "type": "zip", 798 | "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", 799 | "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", 800 | "shasum": "" 801 | }, 802 | "require": { 803 | "php": ">=7.2.5", 804 | "symfony/polyfill-ctype": "~1.8", 805 | "symfony/polyfill-intl-grapheme": "~1.0", 806 | "symfony/polyfill-intl-normalizer": "~1.0", 807 | "symfony/polyfill-mbstring": "~1.0", 808 | "symfony/polyfill-php80": "~1.15" 809 | }, 810 | "require-dev": { 811 | "symfony/error-handler": "^4.4|^5.0", 812 | "symfony/http-client": "^4.4|^5.0", 813 | "symfony/translation-contracts": "^1.1|^2", 814 | "symfony/var-exporter": "^4.4|^5.0" 815 | }, 816 | "type": "library", 817 | "autoload": { 818 | "psr-4": { 819 | "Symfony\\Component\\String\\": "" 820 | }, 821 | "files": [ 822 | "Resources/functions.php" 823 | ], 824 | "exclude-from-classmap": [ 825 | "/Tests/" 826 | ] 827 | }, 828 | "notification-url": "https://packagist.org/downloads/", 829 | "license": [ 830 | "MIT" 831 | ], 832 | "authors": [ 833 | { 834 | "name": "Nicolas Grekas", 835 | "email": "p@tchwork.com" 836 | }, 837 | { 838 | "name": "Symfony Community", 839 | "homepage": "https://symfony.com/contributors" 840 | } 841 | ], 842 | "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", 843 | "homepage": "https://symfony.com", 844 | "keywords": [ 845 | "grapheme", 846 | "i18n", 847 | "string", 848 | "unicode", 849 | "utf-8", 850 | "utf8" 851 | ], 852 | "support": { 853 | "source": "https://github.com/symfony/string/tree/v5.3.7" 854 | }, 855 | "funding": [ 856 | { 857 | "url": "https://symfony.com/sponsor", 858 | "type": "custom" 859 | }, 860 | { 861 | "url": "https://github.com/fabpot", 862 | "type": "github" 863 | }, 864 | { 865 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 866 | "type": "tidelift" 867 | } 868 | ], 869 | "time": "2021-08-26T08:00:08+00:00" 870 | } 871 | ], 872 | "packages-dev": [], 873 | "aliases": [], 874 | "minimum-stability": "stable", 875 | "stability-flags": [], 876 | "prefer-stable": false, 877 | "prefer-lowest": false, 878 | "platform": [], 879 | "platform-dev": [], 880 | "plugin-api-version": "2.1.0" 881 | } 882 | --------------------------------------------------------------------------------