├── src └── Soluble │ └── Japha │ ├── Bridge │ ├── Driver │ │ ├── Pjb62 │ │ │ ├── Exception │ │ │ │ ├── InvalidArgumentException.php │ │ │ │ ├── ConnectException.php │ │ │ │ ├── IOException.php │ │ │ │ ├── RuntimeException.php │ │ │ │ ├── IllegalStateException.php │ │ │ │ ├── BrokenConnectionException.php │ │ │ │ ├── IllegalArgumentException.php │ │ │ │ ├── JavaBridgeException.php │ │ │ │ ├── InternalException.php │ │ │ │ └── JavaException.php │ │ │ ├── Utils │ │ │ │ └── HelperFunctions.php │ │ │ ├── JavaProxyProxy.php │ │ │ ├── ParserInterface.php │ │ │ ├── ParserTag.php │ │ │ ├── IteratorProxyFactory.php │ │ │ ├── ExceptionProxy.php │ │ │ ├── ArrayProxyFactory.php │ │ │ ├── InternalJava.php │ │ │ ├── IteratorProxy.php │ │ │ ├── ApplyArg.php │ │ │ ├── ParserString.php │ │ │ ├── SocketChannelP.php │ │ │ ├── ProxyFactory.php │ │ │ ├── SimpleFactory.php │ │ │ ├── GlobalRef.php │ │ │ ├── ExceptionProxyFactory.php │ │ │ ├── CacheEntry.php │ │ │ ├── ArrayProxy.php │ │ │ ├── ObjectIterator.php │ │ │ ├── JavaType.php │ │ │ ├── JavaClass.php │ │ │ ├── ParserFactory.php │ │ │ ├── ThrowExceptionProxyFactory.php │ │ │ ├── CompositeArg.php │ │ │ ├── HttpTunnelHandler.php │ │ │ ├── ChunkedSocketChannel.php │ │ │ ├── SocketChannel.php │ │ │ ├── SocketHandler.php │ │ │ ├── Proxy │ │ │ │ └── DefaultThrowExceptionProxyFactory.php │ │ │ ├── Arg.php │ │ │ ├── EmptyChannel.php │ │ │ ├── NativeParser.php │ │ │ ├── JavaProxy.php │ │ │ ├── SimpleHttpHandler.php │ │ │ └── AbstractJava.php │ │ ├── ClientInterface.php │ │ ├── ConnectionInterface.php │ │ ├── AbstractDriver.php │ │ └── DriverInterface.php │ ├── Exception │ │ ├── ExceptionInterface.php │ │ ├── SqlException.php │ │ ├── RuntimeException.php │ │ ├── ConnectionException.php │ │ ├── NoSuchFieldException.php │ │ ├── NoSuchMethodException.php │ │ ├── UnexpectedException.php │ │ ├── ClassNotFoundException.php │ │ ├── AuthenticationException.php │ │ ├── BrokenConnectionException.php │ │ ├── ConfigurationException.php │ │ ├── InvalidUsageException.php │ │ ├── InvalidArgumentException.php │ │ ├── FileNotFoundException.php │ │ ├── UnsupportedDriverException.php │ │ ├── NotAvailableException.php │ │ ├── JavaExceptionInterface.php │ │ └── JavaException.php │ ├── Socket │ │ ├── StreamSocketInterface.php │ │ └── StreamSocket.php │ ├── Adapter │ │ └── System.php │ ├── Http │ │ └── Cookie.php │ └── Adapter.php │ ├── Util │ ├── Exception │ │ ├── InvalidArgumentException.php │ │ └── UnsupportedTzException.php │ └── TimeZone.php │ ├── Interfaces │ ├── JavaType.php │ ├── JavaClass.php │ └── JavaObject.php │ └── Db │ └── DriverManager.php ├── infection.json ├── .editorconfig ├── .github └── workflows │ └── publish-doc.yml ├── LICENSE.md ├── CONTRIBUTING.md ├── composer.json └── README.md /src/Soluble/Japha/Bridge/Driver/Pjb62/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | :). 21 | */ 22 | public function getStreamAddress(): string; 23 | } 24 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Interfaces/JavaType.php: -------------------------------------------------------------------------------- 1 | 955) { 30 | return substr($str, 0, 475).'[...]'.substr($str, -475); 31 | } 32 | 33 | return $str; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/publish-doc.yml: -------------------------------------------------------------------------------- 1 | name: publish-doc 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - "doc/**" 9 | - "mkdocs.yml" 10 | - ".github/workflows/**" 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Set up Python 3.5 23 | uses: actions/setup-python@v1 24 | with: 25 | python-version: "3.5" 26 | 27 | - name: Install dependencies 28 | run: | 29 | pip3 install -r doc/_config/requirements.txt 30 | 31 | - name: Build the doc 32 | run: | 33 | mkdocs build 34 | 35 | - name: Deploy 36 | uses: peaceiris/actions-gh-pages@v3 37 | with: 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | publish_dir: ./build/doc 40 | allow_empty_commit: true 41 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Interfaces/JavaClass.php: -------------------------------------------------------------------------------- 1 | anInvalidMethod. java.lang.NoSuchMethodException: anInvalidMethod() 23 | * - cause = java.lang.NoSuchMethodException: anInvalidMethod() 24 | * 25 | * @return string 26 | */ 27 | public function getCause(): string; 28 | 29 | /** 30 | * Return Java stack trace as string. 31 | * 32 | * @return string 33 | */ 34 | public function getStackTrace(): string; 35 | 36 | /** 37 | * Return the originating Java Exception 38 | * class name (FQCN). 39 | * 40 | * @return string ava exception class name (FQCN) 41 | */ 42 | public function getJavaClassName(): string; 43 | } 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | 4 | ## Recommended workflow 5 | 6 | ### Setup 7 | 8 | 1. Setup a [GitHub account](https://github.com/), if you haven't yet. 9 | 2. Fork the project (i.e from the github project page). 10 | 3. Clone your own fork 11 | 12 | ### Test 13 | 14 | Setup a local JasperBridgeServer and copy ./phpunit.xml.dist in 15 | ./phpunit.xml (edit config as needed). 16 | 17 | Check phpunit works by running 18 | 19 | ```shell 20 | $ ./vendor/bin/phpunit 21 | ``` 22 | 23 | ### Optional: db install 24 | 25 | ``` 26 | mysql -u -p -e 'create database phpunit_soluble_test_db' 27 | zcat test/data/mysql/schema.sql.gz | mysql -u -p phpunit_soluble_test_db 28 | zcat test/data/mysql/data.sql.gz | mysql -u -p phpunit_soluble_test_db 29 | ``` 30 | Then activate jdbc tests in `phpunit.xml` 31 | 32 | 33 | ### Source modification 34 | 35 | 1. Create a new branch from master (i.e. feature/24) 36 | 2. Modify the code... Fix, improve :) 37 | 38 | ### Release a P/R (pull request) 39 | 40 | 1. First ensure the code is clean 41 | 42 | ```shell 43 | $ composer fix 44 | $ composer check 45 | ``` 46 | 2. Commit/Push your pull request. 47 | 48 | Thanks !!! 49 | 50 | 51 | ## Keeping your fork up to date 52 | 53 | Best doc [https://help.github.com/articles/syncing-a-fork/](https://help.github.com/articles/syncing-a-fork/) 54 | 55 | ``` 56 | git remote add upstream git://github.com/belgattitude/soluble-japha.git 57 | git fetch upstream 58 | ``` 59 | ### Updating your fork from original repo to keep up with their changes: 60 | 61 | ``` 62 | git checkout master 63 | git merge upstream/master 64 | ``` 65 | ### Push 66 | 67 | ``` 68 | git push 69 | ``` 70 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/Exception/ConnectException.php: -------------------------------------------------------------------------------- 1 | ba = $ba; 38 | $this->timeZone = new TimeZone($ba); 39 | } 40 | 41 | /** 42 | * Get php DateTime helper object. 43 | * 44 | * @return TimeZone 45 | */ 46 | public function getTimeZone(): TimeZone 47 | { 48 | return $this->timeZone; 49 | } 50 | 51 | /** 52 | * Return system default timezone id. 53 | * 54 | * @return string 55 | */ 56 | public function getTimeZoneId(): string 57 | { 58 | return (string) $this->timeZone->getDefault()->getID(); 59 | } 60 | 61 | /** 62 | * Set system default timezone. 63 | * 64 | * @throws UnsupportedTzException 65 | * @throws Bridge\Exception\JavaException 66 | * 67 | * @param string|Interfaces\JavaObject|\DateTimeZone $timezone timezone id, Java(java.util.Timezone) or php DateTimeZone 68 | */ 69 | public function setTimeZoneId($timezone): void 70 | { 71 | $this->timeZone->setDefault($timezone); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/Exception/IllegalArgumentException.php: -------------------------------------------------------------------------------- 1 | getMessage(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/JavaProxyProxy.php: -------------------------------------------------------------------------------- 1 | __client = $client; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ParserInterface.php: -------------------------------------------------------------------------------- 1 | strings = []; 55 | $this->n = 0; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/IteratorProxyFactory.php: -------------------------------------------------------------------------------- 1 | __client->invokeMethod(0, 'ObjectToString', $args); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ArrayProxyFactory.php: -------------------------------------------------------------------------------- 1 | __delegate = $proxy; 45 | $this->__java = $proxy->__java; 46 | $this->__signature = $proxy->__signature; 47 | $this->__client = $proxy->__client; 48 | $this->__hasDeclaredExceptions = $exception; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/InternalJava.php: -------------------------------------------------------------------------------- 1 | __delegate = $proxy; 50 | $this->__java = $proxy->__java; 51 | $this->__signature = $proxy->__signature; 52 | $this->__client = $proxy->__client; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/IteratorProxy.php: -------------------------------------------------------------------------------- 1 | m = $m; 59 | $this->p = $p; 60 | $this->v = $v; 61 | $this->n = $n; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ParserString.php: -------------------------------------------------------------------------------- 1 | getString(); 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getString(): string 71 | { 72 | return substr($this->string, $this->off, $this->length); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/SocketChannelP.php: -------------------------------------------------------------------------------- 1 | getKeepAliveA(); 56 | } 57 | 58 | public function keepAlive(): void 59 | { 60 | $this->keepAliveS(); 61 | $this->checkA($this->peer); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ProxyFactory.php: -------------------------------------------------------------------------------- 1 | createInternal($proxy); 60 | } 61 | 62 | return $proxy; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/SimpleFactory.php: -------------------------------------------------------------------------------- 1 | client = $client; 55 | } 56 | 57 | /** 58 | * @return JavaProxy|JavaType|Exception\InternalException 59 | */ 60 | public function getProxy($result, ?string $signature, $exception, ?bool $wrap) 61 | { 62 | return $result; 63 | } 64 | 65 | /** 66 | * @param JavaException $result 67 | */ 68 | public function checkResult(JavaException $result): void 69 | { 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/GlobalRef.php: -------------------------------------------------------------------------------- 1 | map = []; 50 | } 51 | 52 | /** 53 | * @param JavaType|object|null $object 54 | * 55 | * @return int|JavaType|object 56 | */ 57 | public function add($object) 58 | { 59 | if (null === $object) { 60 | return 0; 61 | } 62 | 63 | return array_push($this->map, $object); 64 | } 65 | 66 | /** 67 | * @param int $id 68 | * 69 | * @return int|JavaType 70 | */ 71 | public function get($id) 72 | { 73 | if (!$id) { 74 | return 0; 75 | } 76 | 77 | return $this->map[--$id]; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ExceptionProxyFactory.php: -------------------------------------------------------------------------------- 1 | fmt = $fmt; 73 | $this->signature = $signature; 74 | $this->factory = $factory; 75 | $this->resultVoid = $resultVoid; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soluble/japha", 3 | "description": "Soluble PHP Java bridge integration", 4 | "license": "MIT", 5 | "keywords": [ 6 | "java", 7 | "bridge", 8 | "phpjavabridge" 9 | ], 10 | "homepage": "https://github.com/belgattitude/soluble-japha", 11 | "type": "library", 12 | "authors": [ 13 | { 14 | "name": "Sébastien Vanvelthem", 15 | "homepage": "https://github.com/belgattitude" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Soluble\\Japha\\": "src/Soluble/Japha" 21 | } 22 | }, 23 | "autoload-dev": { 24 | "psr-4": { 25 | "SolubleTest\\Japha\\": "test/src/SolubleTest/Japha" 26 | }, 27 | "files": ["test/SolubleTestFactories.php"] 28 | }, 29 | "require": { 30 | "ext-xml": "*", 31 | "php": "^7.4.0 || ^8.0.0", 32 | "psr/log": "^1.1" 33 | }, 34 | "require-dev" : { 35 | "phpunit/phpunit": "^9.5.4", 36 | "friendsofphp/php-cs-fixer": "v2.18.5", 37 | "monolog/monolog": "^2.2.0", 38 | "phpstan/phpstan": "^0.12.84", 39 | "phpstan/phpstan-strict-rules": "^0.12.9", 40 | "phpstan/phpstan-phpunit": "^0.12.17", 41 | "belgattitude/pjbserver-tools": "^3.1.0 || ^4.0", 42 | "infection/infection": "^0.21.5", 43 | "dms/phpunit-arraysubset-asserts": "^v0.2.1" 44 | }, 45 | "scripts": { 46 | "check": [ 47 | "@cs-check", 48 | "@phpstan" 49 | ], 50 | "fix": [ 51 | "@cs-fix" 52 | ], 53 | "test": "vendor/bin/phpunit", 54 | "test:mutation": "vendor/bin/infection --configuration=infection.json --test-framework=phpunit --verbose --min-msi=20 --min-covered-msi=25 --threads=4", 55 | "cs-check": "vendor/bin/php-cs-fixer --diff --dry-run -v fix", 56 | "cs-fix": "vendor/bin/php-cs-fixer -v fix", 57 | "phpstan": "vendor/bin/phpstan analyse -c phpstan.neon src", 58 | "build-doc": "mkdocs build && sami.phar update ./.sami.php", 59 | "apigen": "apigen.phar generate --config=./.apigen.yml", 60 | "sami": "sami.phar update ./.sami.php" 61 | }, 62 | "suggest": { 63 | "monolog/monolog": "PSR-3 comptatible logger" 64 | }, 65 | "archive": { 66 | "exclude": ["infection.local.json", ".travis", "requirements.txt", "psalm.xml", ".sami.php", "phpstan.neon", "test", "doc", ".travis", ".travis.yml", ".codeclimate.yml", ".coveralls.yml", ".scrutinizer.yml", ".php_cs", ".gitignore", "phpcs.xml"] 67 | }, 68 | "config": { 69 | "allow-plugins": { 70 | "infection/extension-installer": true, 71 | "ocramius/package-versions": true 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ArrayProxy.php: -------------------------------------------------------------------------------- 1 | __client->invokeMethod(0, 'offsetExists', $ar); 54 | } 55 | 56 | /** 57 | * @param string|int $idx 58 | * 59 | * @return mixed 60 | */ 61 | public function offsetGet($idx) 62 | { 63 | $ar = [$this, $idx]; 64 | 65 | return $this->__client->invokeMethod(0, 'offsetGet', $ar); 66 | } 67 | 68 | /** 69 | * @param string|int $idx 70 | * @param mixed $val 71 | */ 72 | public function offsetSet($idx, $val) 73 | { 74 | $ar = [$this, $idx, $val]; 75 | 76 | return $this->__client->invokeMethod(0, 'offsetSet', $ar); 77 | } 78 | 79 | /** 80 | * @param string|int $idx 81 | */ 82 | public function offsetUnset($idx) 83 | { 84 | $ar = [$this, $idx]; 85 | 86 | return $this->__client->invokeMethod(0, 'offsetUnset', $ar); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ObjectIterator.php: -------------------------------------------------------------------------------- 1 | var = Pjb62Driver::castPjbInternal($javaObject, 'A'); 58 | } 59 | 60 | public function rewind(): void 61 | { 62 | reset($this->var); 63 | } 64 | 65 | /** 66 | * @return bool 67 | */ 68 | public function valid(): bool 69 | { 70 | return $this->current() !== false; 71 | } 72 | 73 | /** 74 | * @return mixed 75 | */ 76 | public function next() 77 | { 78 | return next($this->var); 79 | } 80 | 81 | /** 82 | * @return int|string 83 | */ 84 | public function key() 85 | { 86 | return key($this->var); 87 | } 88 | 89 | /** 90 | * @return mixed 91 | */ 92 | public function current() 93 | { 94 | return current($this->var); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/JavaType.php: -------------------------------------------------------------------------------- 1 | __client = PjbProxyClient::getInstance()->getClient(); 57 | 58 | $args = []; // no arguments for JavaClass 59 | $this->__delegate = $this->__client->referenceObject($name, $args); 60 | 61 | $this->__java = $this->__delegate->__java; 62 | $this->__signature = $this->__delegate->__signature; 63 | } 64 | 65 | /** 66 | * @return Interfaces\JavaClass Java('java.lang.Class') 67 | */ 68 | public function getClass(): Interfaces\JavaObject 69 | { 70 | return $this->__delegate->getClass(); 71 | } 72 | 73 | /** 74 | * Return class name. 75 | * 76 | * @return string java class name as string 77 | */ 78 | public function getName(): string 79 | { 80 | return (string) $this->__delegate->getName(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Http/Cookie.php: -------------------------------------------------------------------------------- 1 | $cookies if null 29 | * 30 | * @return string|null 31 | */ 32 | public static function getCookiesHeaderLine(array $cookies = null): ?string 33 | { 34 | $cookies = $cookies ?? $_COOKIE; 35 | if (empty($cookies)) { 36 | return null; 37 | } 38 | 39 | $cookieParts = []; 40 | foreach ($cookies as $k => $v) { 41 | $cookieParts[] = self::serializePHPCookies($k, $v); 42 | } 43 | 44 | return 'Cookie: '.implode(';', $cookieParts); 45 | } 46 | 47 | /** 48 | * Escapes $cookieValue taking into account its type to serialize it as a valid cookie value. 49 | * 50 | * @param string $cookieName 51 | * @param mixed $cookieValue 52 | * 53 | * @return string 54 | */ 55 | private static function serializePHPCookies(string $cookieName, $cookieValue): string 56 | { 57 | $cookieParts = []; 58 | $urlEncodedCookieName = urlencode($cookieName); 59 | $valueType = gettype($cookieValue); 60 | switch ($valueType) { 61 | case 'integer': 62 | case 'double': 63 | case 'string': 64 | $urlEncodedCookieValue = urlencode((string) $cookieValue); 65 | $cookieParts[] = "$urlEncodedCookieName=$urlEncodedCookieValue"; 66 | break; 67 | 68 | case 'array': 69 | foreach ($cookieValue as $cookieValueKey => $cookieValueValue) { 70 | $cookieParts[] = self::serializePHPCookies($cookieName."[$cookieValueKey]", $cookieValueValue); 71 | } 72 | break; 73 | 74 | case 'NULL': 75 | $cookieParts[] = "$urlEncodedCookieName="; 76 | break; 77 | 78 | case 'boolean': 79 | $cookieParts[] = "$urlEncodedCookieName=".((bool) $cookieValue ? '1' : '0'); 80 | break; 81 | 82 | // It's a security risk to serialize an object and send it as a cookie 83 | case 'object': 84 | // Intentional fallthrough 85 | default: 86 | $cookieParts[] = "$urlEncodedCookieName=".self::UNSUPPORTED_TYPE_VALUE; 87 | break; 88 | } 89 | 90 | return implode(';', $cookieParts); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ParserFactory.php: -------------------------------------------------------------------------------- 1 | parser = new SimpleParser($handler); 60 | $handler->RUNTIME['PARSER'] = self::PARSER_SIMPLE; 61 | } else { 62 | $this->parser = new NativeParser($handler); 63 | $handler->RUNTIME['PARSER'] = self::PARSER_NATIVE; 64 | } 65 | } 66 | 67 | public function parse(): void 68 | { 69 | $this->parser->parse(); 70 | } 71 | 72 | /** 73 | * @param string $str 74 | * 75 | * @return string 76 | */ 77 | public function getData(string $str): string 78 | { 79 | return $this->parser->getData($str); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Interfaces/JavaObject.php: -------------------------------------------------------------------------------- 1 | getOption('java_prefer_values') || ($result->__hasDeclaredExceptions === 'T')) { 73 | throw $result; 74 | } else { 75 | $msg = 'Unchecked exception detected: '.HelperFunctions::java_truncate($result->__toString()); 76 | // Log error 77 | $this->client->getLogger()->critical("[soluble-japha] $msg (".__METHOD__.')'); 78 | trigger_error($msg, E_USER_WARNING); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/CompositeArg.php: -------------------------------------------------------------------------------- 1 | type = $type; 72 | $this->val = []; 73 | $this->counter = 0; 74 | } 75 | 76 | public function setNextIndex(): void 77 | { 78 | $this->idx = $this->counter++; 79 | } 80 | 81 | /** 82 | * @param mixed $val 83 | */ 84 | public function setIndex($val): void 85 | { 86 | $this->idx = $val; 87 | } 88 | 89 | /** 90 | * @param mixed $val 91 | */ 92 | public function linkResult(&$val): void 93 | { 94 | $this->val[$this->idx] = &$val; 95 | } 96 | 97 | public function setResult($val): void 98 | { 99 | $this->val[$this->idx] = $this->factory->getProxy($val, $this->signature, $this->exception, true); 100 | $this->factory = $this->client->simpleFactory; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/HttpTunnelHandler.php: -------------------------------------------------------------------------------- 1 | hasContentLength) { 50 | $data = fread($this->socket, $this->headers['content_length']); 51 | if ($data === false) { 52 | throw new BrokenConnectionException( 53 | 'Cannot read from socket.' 54 | ); 55 | } 56 | 57 | return $data; 58 | } else { 59 | return parent::fread($size); 60 | } 61 | } 62 | 63 | /** 64 | * @throws BrokenConnectionException 65 | */ 66 | public function fwrite(string $data): ?int 67 | { 68 | if ($this->hasContentLength) { 69 | $return = fwrite($this->socket, $data); 70 | if ($return === false) { 71 | throw new BrokenConnectionException( 72 | 'Cannot write from socket.' 73 | ); 74 | } 75 | } else { 76 | return parent::fwrite($data); 77 | } 78 | } 79 | 80 | public function close(): void 81 | { 82 | if ($this->hasContentLength) { 83 | fwrite($this->socket, "0\r\n\r\n"); 84 | fclose($this->socket); 85 | } else { 86 | parent::close(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/ChunkedSocketChannel.php: -------------------------------------------------------------------------------- 1 | peer, "${len}\r\n${data}\r\n"); 56 | if (!$written) { 57 | $msg = 'Cannot write to socket'; 58 | throw new Exception\RuntimeException($msg); 59 | } 60 | 61 | return $written; 62 | } 63 | 64 | /** 65 | * @throws BrokenConnectionException 66 | */ 67 | public function fread(int $size): ?string 68 | { 69 | $line = fgets($this->peer, $this->recv_size); 70 | if ($line === false) { 71 | throw new BrokenConnectionException( 72 | 'Cannot read from socket' 73 | ); 74 | } 75 | 76 | $length = (int) hexdec($line); 77 | $data = ''; 78 | while ($length > 0) { 79 | $str = fread($this->peer, $length); 80 | if (feof($this->peer) || $str === false) { 81 | return null; 82 | } 83 | $length -= strlen($str); 84 | $data .= $str; 85 | } 86 | fgets($this->peer, 3); 87 | 88 | return $data; 89 | } 90 | 91 | public function keepAlive(): void 92 | { 93 | $this->keepAliveSC(); 94 | $this->checkE(); 95 | fclose($this->peer); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Exception/JavaException.php: -------------------------------------------------------------------------------- 1 | setCause($javaCause); 61 | $this->setStackTrace($stackTrace); 62 | $this->setJavaClassName($javaClassName); 63 | if ($driverException !== null) { 64 | $this->setDriverException($driverException); 65 | } 66 | } 67 | 68 | /** 69 | * Set original exception as throw by the driver. 70 | * 71 | * @param Exception $driverException 72 | */ 73 | private function setDriverException(Exception $driverException): void 74 | { 75 | $this->driverException = $driverException; 76 | } 77 | 78 | /** 79 | * Return underlying driver exception. 80 | * 81 | * @return Exception|null 82 | */ 83 | public function getDriverException(): ?Exception 84 | { 85 | return $this->driverException; 86 | } 87 | 88 | /** 89 | * Set Java cause. 90 | */ 91 | private function setCause(string $cause): void 92 | { 93 | $this->cause = $cause; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function getCause(): string 100 | { 101 | return $this->cause; 102 | } 103 | 104 | /** 105 | * Set JVM Stack trace. 106 | */ 107 | private function setStackTrace(string $stackTrace): void 108 | { 109 | $this->stackTrace = $stackTrace; 110 | } 111 | 112 | /** 113 | * {@inheritdoc} 114 | */ 115 | public function getStackTrace(): string 116 | { 117 | return $this->stackTrace; 118 | } 119 | 120 | /** 121 | * {@inheritdoc} 122 | */ 123 | public function getJavaClassName(): string 124 | { 125 | return $this->javaClassName; 126 | } 127 | 128 | /** 129 | * Set java exception class name. 130 | */ 131 | private function setJavaClassName(string $javaClassName): void 132 | { 133 | $this->javaClassName = $javaClassName; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Db/DriverManager.php: -------------------------------------------------------------------------------- 1 | ba = $ba; 37 | } 38 | 39 | /** 40 | * Create an sql connection to database. 41 | * 42 | * 43 | * @throws Exception\JavaException 44 | * @throws Exception\ClassNotFoundException 45 | * @throws Exception\InvalidArgumentException 46 | * @throws Exception\BrokenConnectionException 47 | * 48 | * @param string $dsn 49 | * @param string $driverClass 50 | * 51 | * @return Interfaces\JavaObject Java('java.sql.Connection') 52 | */ 53 | public function createConnection(string $dsn, string $driverClass = 'com.mysql.jdbc.Driver'): Interfaces\JavaObject 54 | { 55 | if (!is_string($dsn) || trim($dsn) == '') { 56 | $message = 'DSN param must be a valid (on-empty) string'; 57 | throw new Exception\InvalidArgumentException(__METHOD__.' '.$message); 58 | } 59 | 60 | $class = $this->ba->javaClass('java.lang.Class'); 61 | try { 62 | $class->forName($driverClass); 63 | } catch (Exception\JavaException $e) { 64 | throw $e; 65 | } 66 | 67 | try { 68 | $conn = $this->getDriverManager()->getConnection($dsn); 69 | } catch (Exception\JavaExceptionInterface $e) { 70 | throw $e; 71 | } 72 | 73 | return $conn; 74 | } 75 | 76 | /** 77 | * Return underlying java driver manager. 78 | * 79 | * @return Interfaces\JavaObject Java('java.sql.DriverManager') 80 | */ 81 | public function getDriverManager(): Interfaces\JavaObject 82 | { 83 | if ($this->driverManager === null) { 84 | $this->driverManager = $this->ba->javaClass('java.sql.DriverManager'); 85 | } 86 | 87 | return $this->driverManager; 88 | } 89 | 90 | /** 91 | * Return a JDBC DSN formatted string from options. 92 | * 93 | * @param string $driver driver name (mysql/mariadb/oracle/postgres...) 94 | * @param string $db database name 95 | * @param string $host server ip or name 96 | * @param string $user username to connect 97 | * @param string $password password to connect 98 | * @param array $options extra options as an associative array 99 | * 100 | * @return string 101 | */ 102 | public static function getJdbcDsn(string $driver, string $db, string $host, string $user, string $password, array $options = []): string 103 | { 104 | $extras = ''; 105 | if (count($options) > 0) { 106 | $tmp = []; 107 | foreach ($options as $key => $value) { 108 | $tmp[] = urlencode($key).'='.urlencode($value); 109 | } 110 | $extras = '&'.implode('&', $tmp); 111 | } 112 | 113 | return "jdbc:$driver://$host/$db?user=$user&password=$password".$extras; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/SocketChannel.php: -------------------------------------------------------------------------------- 1 | peer = $peer; 73 | $this->host = $host; 74 | $this->recv_size = $recv_size; 75 | $this->send_size = $send_size; 76 | } 77 | 78 | /** 79 | * @throws BrokenConnectionException 80 | */ 81 | public function fwrite(string $data): ?int 82 | { 83 | $written = @fwrite($this->peer, $data); 84 | if ($written === false) { 85 | PjbProxyClient::unregisterAndThrowBrokenConnectionException( 86 | sprintf( 87 | 'Broken socket communication with the php-java-bridge while reading socket (%s).', 88 | __METHOD__ 89 | ) 90 | ); 91 | } 92 | 93 | return $written; 94 | } 95 | 96 | public function fread(int $size): ?string 97 | { 98 | $read = @fread($this->peer, $size); 99 | if ($read === false) { 100 | PjbProxyClient::unregisterAndThrowBrokenConnectionException( 101 | sprintf( 102 | 'Broken socket communication with the php-java-bridge while reading socket (%s).', 103 | __METHOD__ 104 | ) 105 | ); 106 | } else { 107 | return $read; 108 | } 109 | } 110 | 111 | public function shutdownBrokenConnection(?string $msg = ''): void 112 | { 113 | if (is_resource($this->peer)) { 114 | fflush($this->peer); 115 | fclose($this->peer); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/SocketHandler.php: -------------------------------------------------------------------------------- 1 | protocol = $protocol; 58 | $this->channel = $channel; 59 | } 60 | 61 | public function write(string $data): ?int 62 | { 63 | return $this->channel->fwrite($data); 64 | } 65 | 66 | public function fwrite(string $data): ?int 67 | { 68 | return $this->write($data); 69 | } 70 | 71 | /** 72 | * @throws BrokenConnectionException 73 | */ 74 | public function read(int $size): string 75 | { 76 | $str = $this->channel->fread($size); 77 | if ($str === null) { 78 | $this->shutdownBrokenConnection('Cannot read from socket'); 79 | } else { 80 | return $str; 81 | } 82 | } 83 | 84 | public function fread(int $size): ?string 85 | { 86 | return $this->read($size); 87 | } 88 | 89 | public function redirect(): void 90 | { 91 | } 92 | 93 | public function getKeepAlive(): string 94 | { 95 | return $this->channel->getKeepAlive(); 96 | } 97 | 98 | public function keepAlive(): void 99 | { 100 | $this->channel->keepAlive(); 101 | } 102 | 103 | /** 104 | * @throws BrokenConnectionException|AuthenticationException 105 | * 106 | * @return never 107 | */ 108 | public function shutdownBrokenConnection(string $msg = '', int $code = null): void 109 | { 110 | $msg = $msg ?? 'Broken connection: Unkown error, please see back end log for detail'; 111 | 112 | // Log error 113 | $client = $this->protocol->getClient(); 114 | $client->getLogger()->critical("[soluble-japha] $msg\" (".__METHOD__.')'); 115 | 116 | $this->channel->shutdownBrokenConnection(); 117 | PjbProxyClient::unregisterAndThrowBrokenConnectionException($msg, $code); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/AbstractDriver.php: -------------------------------------------------------------------------------- 1 | 81 | * $session = $adapter->getDriver()->getJavaSession(); 82 | * $counter = $session->get('counter'); 83 | * if ($adapter->isNull($counter)) { 84 | * $session->put('counter', 1); 85 | * } else { 86 | * $session->put('counter', $counter + 1); 87 | * } 88 | * 89 | * 90 | * @param array $args 91 | * 92 | * @return Interfaces\JavaObject 93 | */ 94 | abstract public function getJavaSession(array $args = []): Interfaces\JavaObject; 95 | 96 | /** 97 | * Cast a java object into a php type. 98 | *(see self::CAST_TYPE_*). 99 | * 100 | * @throws \Soluble\Japha\Bridge\Exception\RuntimeException 101 | * 102 | * @param Interfaces\JavaObject|int|float|array|bool $javaObject 103 | * @param string $cast_type 104 | * 105 | * @return mixed 106 | */ 107 | abstract public function cast($javaObject, string $cast_type); 108 | 109 | /** 110 | * {@inheritdoc} 111 | */ 112 | public function isNull(Interfaces\JavaObject $javaObject = null): bool 113 | { 114 | if ($javaObject === null) { 115 | return true; 116 | } 117 | 118 | return $this->values($javaObject) === null; 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | */ 124 | public function isTrue(Interfaces\JavaObject $javaObject): bool 125 | { 126 | $values = $this->values($javaObject); 127 | if (is_int($values) || is_bool($values)) { 128 | return $values == true; 129 | } 130 | 131 | return false; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/Proxy/DefaultThrowExceptionProxyFactory.php: -------------------------------------------------------------------------------- 1 | '/(php.java.bridge.NoSuchProcedureException)|(Cause: java.lang.NoSuchMethodException)/', 29 | 'ClassNotFoundException' => '/Cause: java.lang.ClassNotFoundException/', 30 | //'InvalidArgumentException' => '/^Invoke failed(.*)php.java.bridge.NoSuchProcedureException/', 31 | 'SqlException' => '/^Invoke failed(.*)java.sql.SQLException/', 32 | 'NoSuchFieldException' => '/Cause: java.lang.NoSuchFieldException/', 33 | //'NullPointerException' => '/Cause: java.lang.NullPointerException/' 34 | ]; 35 | 36 | /** 37 | * @param Client $client 38 | * @param LoggerInterface $logger 39 | */ 40 | public function __construct(Client $client, LoggerInterface $logger) 41 | { 42 | parent::__construct($client); 43 | $this->logger = $logger; 44 | } 45 | 46 | /** 47 | * @param Pjb62\Exception\JavaException $result 48 | * 49 | * @throws Exception\JavaExceptionInterface 50 | */ 51 | public function checkResult(Pjb62\Exception\JavaException $result): void 52 | { 53 | throw $this->getExceptionFromResult($result); 54 | } 55 | 56 | /** 57 | * @param Pjb62\Exception\JavaException $result 58 | * 59 | * @return Exception\JavaExceptionInterface 60 | */ 61 | private function getExceptionFromResult(Pjb62\Exception\JavaException $result): Exception\JavaExceptionInterface 62 | { 63 | $message = (string) $result->__get('message')->__toString(); 64 | 65 | $exceptionClass = $this->defaultException; 66 | 67 | foreach ($this->msgPatternsMapping as $key => $pattern) { 68 | if (preg_match($pattern, $message)) { 69 | $exceptionClass = (string) $key; 70 | break; 71 | } 72 | } 73 | 74 | $cls = '\\Soluble\\Japha\\Bridge\\Exception\\'.$exceptionClass; 75 | 76 | // Public message, mask any login/passwords 77 | $message = preg_replace('/user=([^&\ ]+)|password=([^&\ ]+)/', '****', $message); 78 | $stackTrace = $result->getCause()->__toString(); 79 | $code = $result->getCode(); 80 | 81 | $driverException = null; 82 | if ($result instanceof \Exception) { 83 | $driverException = $result; 84 | } 85 | 86 | // Getting original class name from cause 87 | preg_match('/Cause: ([^:]+):/', $message, $matches); 88 | if (count($matches) > 1) { 89 | $javaExceptionClass = $matches[1]; 90 | } else { 91 | $javaExceptionClass = 'Unknown java exception class'; 92 | } 93 | 94 | // Getting cause from message 95 | $tmp = explode('Cause: ', $message); 96 | if (count($tmp) > 1) { 97 | array_shift($tmp); 98 | $cause = trim(implode(', ', $tmp)); 99 | } else { 100 | $cause = $message; 101 | } 102 | $e = new $cls( 103 | $message, 104 | $cause, 105 | $stackTrace, 106 | $javaExceptionClass, 107 | $code, 108 | $driverException, 109 | null 110 | ); 111 | 112 | $this->logException($e, $exceptionClass); 113 | 114 | return $e; 115 | } 116 | 117 | private function logException(\Throwable $e, string $exceptionClass): void 118 | { 119 | $this->logger->error(sprintf( 120 | '[soluble-japha] Encountered exception %s: %s, code %s (%s)', 121 | $exceptionClass, 122 | $e->getMessage(), 123 | $e->getCode() ?? '?', 124 | get_class($e) 125 | )); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/Arg.php: -------------------------------------------------------------------------------- 1 | client = $client; 75 | $this->factory = $client->simpleFactory; 76 | } 77 | 78 | /** 79 | * @param mixed $val 80 | */ 81 | public function linkResult(&$val): void 82 | { 83 | $this->val = &$val; 84 | } 85 | 86 | /** 87 | * @param mixed $val 88 | */ 89 | public function setResult($val): void 90 | { 91 | $this->val = &$val; 92 | } 93 | 94 | /** 95 | * @param bool $wrap 96 | * 97 | * @return JavaType|string 98 | */ 99 | public function getResult(bool $wrap) 100 | { 101 | $rc = $this->factory->getProxy($this->val, $this->signature, $this->exception, $wrap); 102 | 103 | $factory = $this->factory; 104 | $this->factory = $this->client->simpleFactory; 105 | if ($rc instanceof \Soluble\Japha\Bridge\Driver\Pjb62\Exception\JavaException) { 106 | $factory->checkResult($rc); 107 | } 108 | 109 | return $rc; 110 | } 111 | 112 | /** 113 | * @param SimpleFactory $factory 114 | */ 115 | public function setFactory(SimpleFactory $factory): void 116 | { 117 | $this->factory = $factory; 118 | } 119 | 120 | /** 121 | * @param string $string 122 | */ 123 | public function setException(string $string): void 124 | { 125 | $this->exception = $string; 126 | } 127 | 128 | public function setVoidSignature(): void 129 | { 130 | $this->signature = '@V'; 131 | $key = $this->client->currentCacheKey; 132 | if ($key && $key[0] !== '~') { 133 | $this->client->currentArgumentsFormat[6] = '3'; 134 | $cacheEntry = new CacheEntry($this->client->currentArgumentsFormat, $this->signature, $this->factory, true); 135 | $this->client->methodCache[$key] = $cacheEntry; 136 | } 137 | } 138 | 139 | public function setSignature(string $signature): void 140 | { 141 | $this->signature = $signature; 142 | $key = $this->client->currentCacheKey; 143 | if ($key && $key[0] !== '~') { 144 | $cacheEntry = new CacheEntry($this->client->currentArgumentsFormat, $this->signature, $this->factory, false); 145 | $this->client->methodCache[$key] = $cacheEntry; 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/EmptyChannel.php: -------------------------------------------------------------------------------- 1 | send_size = $send_size; 75 | $this->recv_size = $recv_size; 76 | $this->handler = $handler; 77 | } 78 | 79 | public function fwrite(string $data): ?int 80 | { 81 | return $this->handler->fwrite($data); 82 | } 83 | 84 | public function fread(int $size): ?string 85 | { 86 | return $this->handler->fread($size); 87 | } 88 | 89 | protected function getKeepAliveA(): string 90 | { 91 | return ''; 92 | } 93 | 94 | protected function getKeepAliveE(): string 95 | { 96 | return ''; 97 | } 98 | 99 | public function getKeepAlive(): string 100 | { 101 | return $this->getKeepAliveE(); 102 | } 103 | 104 | /** 105 | * @throws Exception\RuntimeException 106 | */ 107 | protected function error(): void 108 | { 109 | $msg = 'An unchecked exception occured during script execution. Please check the server log files for details.'; 110 | throw new Exception\RuntimeException($msg); 111 | } 112 | 113 | protected function checkA($peer): void 114 | { 115 | $val = $this->res[6]; 116 | if ($val !== 'A') { 117 | fclose($peer); 118 | } 119 | if ($val !== 'A' && $val !== 'E') { 120 | $this->error(); 121 | } 122 | } 123 | 124 | public function checkE(): void 125 | { 126 | $val = $this->res[6]; 127 | if ($val !== 'E') { 128 | $this->error(); 129 | } 130 | } 131 | 132 | protected function keepAliveS(): void 133 | { 134 | $this->res = $this->fread(10); 135 | } 136 | 137 | protected function keepAliveSC(): void 138 | { 139 | $this->res = $this->fread(10); 140 | $this->fwrite(''); 141 | $this->fread($this->recv_size); 142 | } 143 | 144 | protected function keepAliveH(): void 145 | { 146 | $this->res = $this->handler->read(10); 147 | } 148 | 149 | public function keepAlive(): void 150 | { 151 | $this->keepAliveH(); 152 | $this->checkE(); 153 | } 154 | 155 | public function shutdownBrokenConnection(string $msg = ''): void 156 | { 157 | // so nothing 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/NativeParser.php: -------------------------------------------------------------------------------- 1 | client = $client; 80 | $this->parser = xml_parser_create(); 81 | xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 82 | xml_set_object($this->parser, $this); 83 | xml_set_element_handler($this->parser, 'begin', 'end'); 84 | xml_parse($this->parser, ''); 85 | $this->level = 0; 86 | $this->java_recv_size = $client->java_recv_size; 87 | } 88 | 89 | /** 90 | * @param resource $parser 91 | * @param string $name 92 | * @param mixed $param 93 | */ 94 | protected function begin($parser, $name, $param): void 95 | { 96 | $this->event = true; 97 | switch ($name) { 98 | case 'X': 99 | case 'A': 100 | ++$this->level; 101 | break; 102 | } 103 | $this->client->begin($name, $param); 104 | } 105 | 106 | /** 107 | * @param resource $parser 108 | * @param string $name 109 | */ 110 | public function end($parser, $name): void 111 | { 112 | $this->client->end($name); 113 | switch ($name) { 114 | case 'X': 115 | case 'A': 116 | --$this->level; 117 | } 118 | } 119 | 120 | /** 121 | * @param string $str 122 | * 123 | * @return string 124 | */ 125 | public function getData(string $str): string 126 | { 127 | return base64_decode($str); 128 | } 129 | 130 | public function parse(): void 131 | { 132 | do { 133 | $this->event = false; 134 | $this->buf = $this->client->read($this->java_recv_size); 135 | $len = strlen($this->buf); 136 | if (!xml_parse($this->parser, $this->buf, $len === 0)) { 137 | $this->client->protocol->handler->shutdownBrokenConnection( 138 | sprintf( 139 | 'protocol error: "buf: {%s}", %s at col %d. Check the back end log for OutOfMemoryErrors.', 140 | $this->buf, 141 | xml_error_string(xml_get_error_code($this->parser)), 142 | xml_get_current_column_number($this->parser) 143 | ) 144 | ); 145 | } 146 | } while (!$this->event || $this->level > 0); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/Exception/JavaException.php: -------------------------------------------------------------------------------- 1 | __client = PjbProxyClient::getInstance()->getClient(); 72 | $name = array_shift($args); 73 | if (is_array($name)) { 74 | $args = $name; 75 | $name = array_shift($args); 76 | } 77 | 78 | if (empty($args)) { 79 | parent::__construct($name); 80 | } else { 81 | parent::__construct($args[0]); 82 | } 83 | 84 | $this->__delegate = $this->__client->createObject($name, $args); 85 | $this->__java = $this->__delegate->get__java(); 86 | $this->__signature = $this->__delegate->get__signature(); 87 | $this->__hasDeclaredExceptions = 'T'; 88 | } 89 | 90 | public function __cast(string $type) 91 | { 92 | return $this->__delegate->__cast($type); 93 | } 94 | 95 | public function __sleep() 96 | { 97 | $this->__delegate->__sleep(); 98 | 99 | return ['__delegate']; 100 | } 101 | 102 | public function __wakeup() 103 | { 104 | $this->__delegate->__wakeup(); 105 | $this->__java = $this->__delegate->__java; 106 | $this->__client = $this->__delegate->__client; 107 | } 108 | 109 | public function __get(string $key) 110 | { 111 | return $this->__delegate->__get($key); 112 | } 113 | 114 | public function __set(string $key, $val): void 115 | { 116 | $this->__delegate->__set($key, $val); 117 | } 118 | 119 | public function __call(string $method, array $args) 120 | { 121 | return $this->__delegate->__call($method, $args); 122 | } 123 | 124 | /** 125 | * @return string 126 | */ 127 | public function __toString(): string 128 | { 129 | return (string) $this->__delegate->__toExceptionString($this->getTraceAsString()); 130 | } 131 | 132 | /** 133 | * @return int 134 | */ 135 | public function get__java(): int 136 | { 137 | return $this->__java; 138 | } 139 | 140 | /** 141 | * Return java object id. 142 | * 143 | * @return int 144 | */ 145 | public function __getJavaInternalObjectId(): int 146 | { 147 | return $this->__java; 148 | } 149 | 150 | /** 151 | * @return string|null 152 | */ 153 | public function get__signature(): ?string 154 | { 155 | return $this->__signature; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/DriverInterface.php: -------------------------------------------------------------------------------- 1 | [ 22 | 'protocol_version' => '1.1', 23 | ] 24 | ]; 25 | 26 | public const DEFAULT_CONNECT_TIMEOUTS = [ 27 | 'HOST_127.0.0.1' => 5.0, 28 | 'HOST_localhost' => 5.0, 29 | 'DEFAULT' => 20.0 30 | ]; 31 | 32 | /** 33 | * @var resource 34 | */ 35 | protected $socket; 36 | 37 | protected $persistent = false; 38 | protected $address; 39 | protected $connectTimeout; 40 | protected $streamContext; 41 | protected $transport; 42 | 43 | /** 44 | * @param string $transport tcp, ssl... see self::TRANSPORT 45 | * @param string $address ip:port 46 | * @param float $connectTimeout connection timeout in seconds (double: i.e: 5.0) 47 | * @param mixed[] $streamContext see stream_context_create() 48 | * @param bool $persistent whether to use persistent connections 49 | */ 50 | public function __construct( 51 | string $transport, 52 | string $address, 53 | float $connectTimeout = null, 54 | array $streamContext = self::DEFAULT_CONTEXT, 55 | bool $persistent = false 56 | ) { 57 | $this->setTransport($transport); 58 | $this->address = $address; 59 | $this->setConnectTimeout($connectTimeout); 60 | $this->streamContext = $streamContext; 61 | $this->persistent = $persistent; 62 | $this->createSocket(); 63 | } 64 | 65 | protected function setConnectTimeout(float $timeout = null): void 66 | { 67 | if ($timeout === null) { 68 | list($host) = explode(':', $this->address); 69 | if (array_key_exists("HOST_$host", self::DEFAULT_CONNECT_TIMEOUTS)) { 70 | $timeout = self::DEFAULT_CONNECT_TIMEOUTS["HOST_$host"]; 71 | } else { 72 | $timeout = self::DEFAULT_CONNECT_TIMEOUTS['DEFAULT']; 73 | } 74 | } 75 | $this->connectTimeout = $timeout; 76 | } 77 | 78 | /** 79 | * @throws InvalidArgumentException when getting an unsupported transport 80 | */ 81 | protected function setTransport(string $transport): void 82 | { 83 | if (!in_array($transport, self::REGISTERED_TRANSPORTS, true)) { 84 | throw new InvalidArgumentException(sprintf( 85 | 'Unsupported transport "%s" given (supported: %s)', 86 | $transport, 87 | implode(',', array_keys(self::REGISTERED_TRANSPORTS)) 88 | )); 89 | } 90 | $this->transport = $transport; 91 | } 92 | 93 | public function getTransport(): string 94 | { 95 | return $this->transport; 96 | } 97 | 98 | public function getConnectTimeout(): float 99 | { 100 | return $this->connectTimeout; 101 | } 102 | 103 | /** 104 | * @return resource php socket 105 | */ 106 | public function getSocket() 107 | { 108 | return $this->socket; 109 | } 110 | 111 | public function getStreamAddress(): string 112 | { 113 | return sprintf('%s://%s', $this->getTransport(), $this->address); 114 | } 115 | 116 | /** 117 | * Whether the connection is persistent or not. 118 | * 119 | * @return bool 120 | */ 121 | public function isPersistent(): bool 122 | { 123 | return $this->persistent; 124 | } 125 | 126 | /** 127 | * @throws ConnectionException 128 | */ 129 | protected function createSocket(): void 130 | { 131 | $socket = @stream_socket_client( 132 | $this->getStreamAddress(), 133 | $errno, 134 | $errstr, 135 | $this->connectTimeout, 136 | $this->getStreamFlags(), 137 | $this->getStreamContext() 138 | ); 139 | 140 | if ($socket === false || !is_resource($socket)) { 141 | $msg = sprintf( 142 | "Could not connect to the php-java-bridge server '%s'. Please start it. (err: %s, %s)", 143 | $this->address, 144 | $errno ?? 0, 145 | $errstr ?? 'Empty errstr returned' 146 | ); 147 | throw new ConnectionException($msg); 148 | } 149 | 150 | $this->socket = $socket; 151 | } 152 | 153 | protected function getStreamFlags(): int 154 | { 155 | return $this->persistent ? STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT : STREAM_CLIENT_CONNECT; 156 | } 157 | 158 | /** 159 | * @return resource 160 | */ 161 | protected function getStreamContext() 162 | { 163 | return stream_context_create($this->streamContext); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/JavaProxy.php: -------------------------------------------------------------------------------- 1 | __java = $java; 90 | $this->__signature = $signature; 91 | $this->__client = PjbProxyClient::getInstance()->getClient(); 92 | } 93 | 94 | /** 95 | * @param string $type 96 | * 97 | * @return mixed 98 | */ 99 | public function __cast(string $type) 100 | { 101 | return $this->__client->cast($this, $type); 102 | } 103 | 104 | /** 105 | * @return array 106 | */ 107 | public function __sleep() 108 | { 109 | $args = [$this, HelperFunctions::java_get_session_lifetime()]; 110 | $this->__serialID = $this->__client->invokeMethod(0, 'serialize', $args); 111 | $this->__tempGlobalRef = $this->__client->globalRef; 112 | 113 | return ['__serialID', '__tempGlobalRef']; 114 | } 115 | 116 | public function __wakeup() 117 | { 118 | $args = [$this->__serialID, HelperFunctions::java_get_session_lifetime()]; 119 | $this->__client = PjbProxyClient::getInstance()->getClient(); 120 | if ($this->__tempGlobalRef) { 121 | $this->__client->globalRef = $this->__tempGlobalRef; 122 | } 123 | $this->__tempGlobalRef = null; 124 | $this->__java = $this->__client->invokeMethod(0, 'deserialize', $args); 125 | } 126 | 127 | /** 128 | * Automatically detroy this object 129 | * by delegating the unref to the bridge side. 130 | */ 131 | public function __destruct() 132 | { 133 | if (isset($this->__client)) { 134 | $this->__client->unref($this->__java); 135 | } 136 | } 137 | 138 | /** 139 | * @param string $key 140 | * 141 | * @return mixed 142 | */ 143 | public function __get(string $key) 144 | { 145 | return $this->__client->getProperty($this->__java, $key); 146 | } 147 | 148 | /** 149 | * @param string $key 150 | * @param mixed $val 151 | */ 152 | public function __set(string $key, $val): void 153 | { 154 | $this->__client->setProperty($this->__java, $key, $val); 155 | } 156 | 157 | public function __call(string $method, array $args) 158 | { 159 | return $this->__client->invokeMethod($this->__java, $method, $args); 160 | } 161 | 162 | /** 163 | * @return string 164 | */ 165 | public function __toString(): string 166 | { 167 | try { 168 | return (string) $this->__client->invokeMethod(0, 'ObjectToString', [$this]); 169 | } catch (Exception\JavaException $ex) { 170 | $msg = 'Exception in Java::__toString(): '.HelperFunctions::java_truncate((string) $ex); 171 | $this->__client->getLogger()->warning("[soluble-japha] $msg (".__METHOD__.')'); 172 | trigger_error($msg, E_USER_WARNING); 173 | 174 | return ''; 175 | } 176 | } 177 | 178 | /** 179 | * @return int 180 | */ 181 | public function get__java(): int 182 | { 183 | return $this->__java; 184 | } 185 | 186 | /** 187 | * Return java object id. 188 | * 189 | * @return int 190 | */ 191 | public function __getJavaInternalObjectId(): int 192 | { 193 | return $this->__java; 194 | } 195 | 196 | /** 197 | * @return string 198 | */ 199 | public function get__signature(): ?string 200 | { 201 | return $this->__signature; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Util/TimeZone.php: -------------------------------------------------------------------------------- 1 | ba = $ba; 58 | $this->timeZoneClass = $ba->javaClass('java.util.TimeZone'); 59 | } 60 | 61 | /** 62 | * Return java available timezone ids. 63 | * 64 | * @return array 65 | */ 66 | public function getAvailableIDs(): array 67 | { 68 | if ($this->availableTz === null) { 69 | $this->availableTz = []; 70 | $available = $this->timeZoneClass->getAvailableIDs(); 71 | foreach ($available as $id) { 72 | $this->availableTz[] = (string) $id; 73 | } 74 | } 75 | 76 | return $this->availableTz; 77 | } 78 | 79 | /** 80 | * Return default jvm TimeZone. 81 | * 82 | * If TimeZone::enableTzCache() is active (by default), 83 | * the default JVM timezone object will be locally cached on the 84 | * PHP side for performance reasons. 85 | * 86 | * The behaviour could potentially lead to unexpected results if 87 | * your code modify the default JVM timezone during execution. 88 | * (i.e. by calling java.util.TimeZone::setDefault() in your code). 89 | * 90 | * If you don't want to rely on automatic timezone caching, you can 91 | * disable it at bootstrap (Soluble\Japha\Util\TimeZone::disableTzCache) 92 | * or simply call this method with $enableTzCache=false 93 | * 94 | * 95 | * @param bool $enableTzCache enable local caching of default timezone 96 | * 97 | * @return Interfaces\JavaObject Java('java.util.TimeZone') 98 | */ 99 | public function getDefault($enableTzCache = true): Interfaces\JavaObject 100 | { 101 | $enableCache = $enableTzCache && self::$enableTzCache; 102 | if (!$enableCache || self::$defaultTz === null) { 103 | self::$defaultTz = $this->timeZoneClass->getDefault(); 104 | } 105 | 106 | return self::$defaultTz; 107 | } 108 | 109 | /** 110 | * Create a Java(java.util.TimeZone) object from id. 111 | * 112 | * @throws Exception\UnsupportedTzException 113 | * @throws Exception\InvalidArgumentException 114 | * @throws \Soluble\Japha\Bridge\Exception\JavaException 115 | * 116 | * @param string|DateTimeZone $id string identifier or php DateTimeZone 117 | * 118 | * @return Interfaces\JavaObject Java('java.util.TimeZone') 119 | */ 120 | public function getTimeZone($id): Interfaces\JavaObject 121 | { 122 | if ($id instanceof DateTimeZone) { 123 | $phpTimezone = $id->getName(); 124 | } elseif (is_string($id) && trim($id) != '') { 125 | $phpTimezone = $id; 126 | } else { 127 | throw new Exception\InvalidArgumentException('Method getTimeZone($id) require argument to be datetimeZone or a non empty string'); 128 | } 129 | 130 | /** 131 | * @var Interfaces\JavaClass 132 | */ 133 | $tz = $this->timeZoneClass->getTimeZone($phpTimezone); 134 | 135 | /** 136 | * @var string 137 | */ 138 | $javaTimezone = (string) $tz->getID(); 139 | if ($javaTimezone === 'GMT' && $phpTimezone !== 'GMT') { 140 | $msg = sprintf( 141 | "The timezone id '%s' could not be understood by JVM (JVM returned defaulted to GMT)", 142 | $id instanceof DateTimeZone ? $id->getName() : $id 143 | ); 144 | throw new Exception\UnsupportedTzException($msg); 145 | } 146 | 147 | return $tz; 148 | } 149 | 150 | /** 151 | * Set default JVM/servlet timezone. 152 | * 153 | * @throws \Soluble\Japha\Bridge\Exception\JavaException 154 | * @throws Exception\UnsupportedTzException 155 | * 156 | * @param string|Interfaces\JavaObject|DateTimeZone $timeZone timezone id, Java(java.util.Timezone) or php DateTimeZone 157 | */ 158 | public function setDefault($timeZone): void 159 | { 160 | if (is_string($timeZone) || $timeZone instanceof DateTimeZone) { 161 | $timeZone = $this->getTimeZone($timeZone); 162 | } 163 | $this->timeZoneClass->setDefault($timeZone); 164 | self::$defaultTz = $timeZone; 165 | } 166 | 167 | /** 168 | * Enable local timezone cache. 169 | * 170 | * TimeZone::enableTzCache() enable local object caching 171 | * for defaultTimezone 172 | * 173 | * This behaviour could potentially lead to unexpected results if 174 | * your code modify the default JVM timezone during execution. 175 | * (i.e. by calling java.util.TimeZone::setDefault() in your code). 176 | * 177 | * If you don't want to rely on automatic timezone caching, you can 178 | * disable it at bootstrap (Soluble\Japha\Util\TimeZone::disableTzCache) 179 | */ 180 | public static function enableTzCache(): void 181 | { 182 | self::$enableTzCache = true; 183 | self::$defaultTz = null; 184 | } 185 | 186 | /** 187 | * Disable local timezone cache. 188 | * 189 | * TimeZone::disbaleTzCache() disable local object caching 190 | * for defaultTimezone 191 | */ 192 | public static function disableTzCache(): void 193 | { 194 | self::$enableTzCache = false; 195 | self::$defaultTz = null; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Adapter.php: -------------------------------------------------------------------------------- 1 | Pjb62Driver::class 29 | ]; 30 | 31 | /** 32 | * @var Driver\AbstractDriver 33 | */ 34 | protected $driver; 35 | 36 | /** 37 | * @var Adapter\System 38 | */ 39 | protected $system; 40 | 41 | /** 42 | * @var LoggerInterface 43 | */ 44 | protected $logger; 45 | 46 | /** 47 | * Constructor. 48 | * 49 | * 50 | * $ba = new Adapter([ 51 | * 'driver' => 'Pjb62', 52 | * 'servlet_address' => 'http://127.0.0.1:8080/javabridge-bundle/java/servlet.phpjavabridge' 53 | * //'use_persistent_connection' => false 54 | * //'java_default_timezone' => null, 55 | * //'java_prefer_values' => true, 56 | * //'force_simple_xml_parser' => false 57 | * //'java_log_level' => null // set it to 0,1,2,3,4,5,6 to see errors in tomcat log 58 | * ]); 59 | * 60 | * 61 | * 62 | * 63 | * @throws Exception\UnsupportedDriverException 64 | * @throws Exception\InvalidArgumentException 65 | * @throws Exception\ConfigurationException 66 | * @throws Exception\ConnectionException 67 | * 68 | * @param array $options 69 | * @param LoggerInterface $logger any PSR-3 compatible logger 70 | */ 71 | public function __construct(array $options, LoggerInterface $logger = null) 72 | { 73 | if ($logger === null) { 74 | $logger = new NullLogger(); 75 | } 76 | $this->logger = $logger; 77 | 78 | $driver = !isset($options['driver']) ? self::DEFAULT_DRIVER : strtolower($options['driver']); 79 | 80 | $driver_class = self::$registeredDrivers[$driver] ?? null; 81 | 82 | if ($driver_class === null) { 83 | throw new Exception\UnsupportedDriverException(__METHOD__." Driver '$driver' is not supported"); 84 | } 85 | 86 | $this->driver = new $driver_class($options, $logger); 87 | 88 | if (array_key_exists('java_default_timezone', $options) 89 | && $options['java_default_timezone'] !== null) { 90 | $this->setJavaDefaultTimezone($options['java_default_timezone']); 91 | } 92 | } 93 | 94 | /** 95 | * Create a new Java instance from a FQCN (constructor arguments are sent in a variadic way). 96 | * 97 | * 98 | * $hash = $ba->java('java.util.HashMap', ['key' => '保éà']); 99 | * echo $hash->get('key'); // prints "保éà" 100 | * 101 | * 102 | * @param string $class Java class name (FQCN) 103 | * @param mixed|null ...$args arguments passed to the constructor of the java object 104 | * 105 | * @throws \Soluble\Japha\Bridge\Exception\JavaException 106 | * @throws \Soluble\Japha\Bridge\Exception\ClassNotFoundException 107 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 108 | * 109 | * @see \Soluble\Japha\Bridge\Adapter::javaClass for information about classes 110 | * 111 | * @return Interfaces\JavaObject 112 | */ 113 | public function java(string $class, ...$args): Interfaces\JavaObject 114 | { 115 | return $this->driver->instanciate($class, ...$args); 116 | } 117 | 118 | /** 119 | * Load a java class. 120 | * 121 | * 122 | * $calendar = $ba->javaClass('java.util.Calendar')->getInstance(); 123 | * $date = $calendar->getTime(); 124 | * 125 | * $system = $ba->javaClass('java.lang.System'); 126 | * echo $system->getProperties()->get('java.vm_name); 127 | * 128 | * $tzClass = $ba->javaClass('java.util.TimeZone'); 129 | * echo $tz->getDisplayName(false, $tzClass->SHORT); 130 | * 131 | * 132 | * @see \Soluble\Japha\Bridge\Adapter::java() for object creation 133 | * 134 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 135 | * @throws \Soluble\Japha\Bridge\Exception\ClassNotFoundException 136 | * 137 | * @param string $class Java class name (FQCN) 138 | * 139 | * @return Interfaces\JavaClass 140 | */ 141 | public function javaClass(string $class): Interfaces\JavaClass 142 | { 143 | return $this->driver->getJavaClass($class); 144 | } 145 | 146 | /** 147 | * Checks whether object is an instance of a class or interface. 148 | * 149 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 150 | * @throws \Soluble\Japha\Bridge\Exception\ClassNotFoundException 151 | * @throws \Soluble\Japha\Bridge\Exception\InvalidArgumentException 152 | * 153 | * @param Interfaces\JavaObject $javaObject 154 | * @param string|Interfaces\JavaObject|Interfaces\JavaClass $className java class name 155 | * 156 | * @return bool 157 | */ 158 | public function isInstanceOf(Interfaces\JavaObject $javaObject, $className): bool 159 | { 160 | return $this->driver->isInstanceOf($javaObject, $className); 161 | } 162 | 163 | /** 164 | * Return object java FQCN class name. 165 | * 166 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 167 | * 168 | * @param Interfaces\JavaObject $javaObject 169 | * 170 | * @return string 171 | */ 172 | public function getClassName(Interfaces\JavaObject $javaObject): string 173 | { 174 | return $this->driver->getClassName($javaObject); 175 | } 176 | 177 | /** 178 | * Whether a java internal value is null. 179 | * 180 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 181 | * 182 | * @param Interfaces\JavaObject|null $javaObject 183 | * 184 | * @return bool 185 | */ 186 | public function isNull(Interfaces\JavaObject $javaObject = null): bool 187 | { 188 | return $this->driver->isNull($javaObject); 189 | } 190 | 191 | /** 192 | * Check wether a java value is true (boolean). 193 | * 194 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 195 | * 196 | * @param Interfaces\JavaObject $javaObject 197 | * 198 | * @return bool 199 | */ 200 | public function isTrue(Interfaces\JavaObject $javaObject): bool 201 | { 202 | return $this->driver->isTrue($javaObject); 203 | } 204 | 205 | /** 206 | * Return system properties. 207 | * 208 | * @return Adapter\System 209 | */ 210 | public function getSystem(): Adapter\System 211 | { 212 | if ($this->system === null) { 213 | $this->system = new Adapter\System($this); 214 | } 215 | 216 | return $this->system; 217 | } 218 | 219 | /** 220 | * Fast retrieval of JavaObject values (one roundtrip), 221 | * use it on Java array structures (ArrayList, HashMap...) 222 | * to avoid the need of iterations on the PHP side. 223 | * 224 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 225 | * 226 | * @param Interfaces\JavaObject $javaObject 227 | * 228 | * @return mixed 229 | */ 230 | public function values(Interfaces\JavaObject $javaObject) 231 | { 232 | return $this->driver->values($javaObject); 233 | } 234 | 235 | /** 236 | * Return underlying driver. 237 | * 238 | * @return Driver\DriverInterface 239 | */ 240 | public function getDriver(): Driver\DriverInterface 241 | { 242 | return $this->driver; 243 | } 244 | 245 | /** 246 | * Set the JVM/Java default timezone. 247 | * 248 | * Caution: this method should be used with care because it will change the global timezone 249 | * on the JVM or Bridge servlet. Better to configure it by other ways as every 250 | * scripts may change it. 251 | * 252 | * 253 | * @throws \Soluble\Japha\Util\Exception\UnsupportedTzException 254 | * @throws \Soluble\Japha\Bridge\Exception\BrokenConnectionException 255 | * 256 | * @param string $timezone 257 | */ 258 | private function setJavaDefaultTimezone(string $timezone): void 259 | { 260 | $this->getSystem()->setTimeZoneId($timezone); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/SimpleHttpHandler.php: -------------------------------------------------------------------------------- 1 | cookies = []; 109 | $this->protocol = $protocol; 110 | $this->ssl = $ssl; 111 | $this->host = $host; 112 | $this->port = $port; 113 | $this->java_servlet = $java_servlet; 114 | 115 | $this->java_send_size = $java_send_size; 116 | $this->java_recv_size = $java_recv_size; 117 | 118 | $this->cachedValues = [ 119 | 'getContext' => null 120 | ]; 121 | $this->createChannel(); 122 | } 123 | 124 | /** 125 | * @throws Exception\IllegalStateException on channel creation error 126 | * @throws Exception\BrokenConnectionException if all goes wrong 127 | */ 128 | protected function createChannel() 129 | { 130 | $channelName = Pjb62Driver::getJavaBridgeHeader('X_JAVABRIDGE_REDIRECT', $_SERVER); 131 | $context = Pjb62Driver::getJavaBridgeHeader('X_JAVABRIDGE_CONTEXT', $_SERVER); 132 | $len = strlen($context); 133 | $len0 = PjbProxyClient::getInstance()->getCompatibilityOption($this->protocol->client); 134 | $len1 = chr($len & 0xFF); 135 | $len >>= 8; 136 | $len2 = chr($len & 0xFF); 137 | $this->channel = $this->getChannel($channelName); 138 | $this->protocol->setSocketHandler(new SocketHandler($this->protocol, $this->channel)); 139 | $this->protocol->write("\177${len0}${len1}${len2}${context}"); 140 | $this->context = sprintf("X_JAVABRIDGE_CONTEXT: %s\r\n", $context); 141 | $this->protocol->handler = $this->protocol->getSocketHandler(); 142 | 143 | if ($this->protocol->client->sendBuffer !== null) { 144 | $ret = $this->protocol->handler->write($this->protocol->client->sendBuffer); 145 | if ($ret === null) { 146 | $this->protocol->handler->shutdownBrokenConnection('Broken local connection handle'); 147 | } 148 | $this->protocol->client->sendBuffer = null; 149 | $this->protocol->handler->read(1); 150 | } 151 | } 152 | 153 | public function getContextFromCgiEnvironment(): string 154 | { 155 | return Pjb62Driver::getJavaBridgeHeader('X_JAVABRIDGE_CONTEXT', $_SERVER); 156 | } 157 | 158 | /** 159 | * @return string 160 | */ 161 | public function getContext() 162 | { 163 | if (!array_key_exists('getContext', $this->cachedValues)) { 164 | $ctx = $this->getContextFromCgiEnvironment(); 165 | if ($ctx) { 166 | $this->cachedValues['getContext'] = sprintf('X_JAVABRIDGE_CONTEXT: %s', $ctx); 167 | } 168 | } 169 | 170 | return $this->cachedValues['getContext']; 171 | } 172 | 173 | public function getWebAppInternal() 174 | { 175 | $context = $this->protocol->webContext; 176 | if (isset($context)) { 177 | return $context; 178 | } 179 | 180 | return ($this->java_servlet == 'User' && 181 | array_key_exists('PHP_SELF', $_SERVER) && 182 | array_key_exists('HTTP_HOST', $_SERVER)) ? $_SERVER['PHP_SELF'].'javabridge' : null; 183 | } 184 | 185 | public function getWebApp(): string 186 | { 187 | $context = $this->getWebAppInternal(); 188 | if (null === $context) { 189 | $context = $this->java_servlet; 190 | } 191 | 192 | return $context; 193 | } 194 | 195 | /** 196 | * @throws Exception\BrokenConnectionException 197 | */ 198 | public function write(string $data): ?int 199 | { 200 | return $this->protocol->getSocketHandler()->write($data); 201 | } 202 | 203 | public function doSetCookie($key, $val, $path) 204 | { 205 | $path = trim($path); 206 | $webapp = $this->getWebAppInternal(); 207 | if (!$webapp) { 208 | $path = '/'; 209 | } 210 | setcookie($key, $val, 0, $path); 211 | } 212 | 213 | /** 214 | * @throws Exception\BrokenConnectionException 215 | */ 216 | public function read(int $size): string 217 | { 218 | return $this->protocol->getSocketHandler()->read($size); 219 | } 220 | 221 | /** 222 | * @param string $channelName generally the tcp port on which to connect 223 | * 224 | * @return SocketChannelP 225 | * 226 | * @throws Exception\ConnectException 227 | */ 228 | public function getChannel(string $channelName): SocketChannelP 229 | { 230 | $persistent = $this->protocol->client->getParam(Client::PARAM_USE_PERSISTENT_CONNECTION); 231 | try { 232 | $streamSocket = new StreamSocket( 233 | $this->ssl === 'ssl://' ? StreamSocket::TRANSPORT_SSL : StreamSocket::TRANSPORT_TCP, 234 | $this->host.':'.$channelName, 235 | null, 236 | [], 237 | $persistent 238 | ); 239 | $socket = $streamSocket->getSocket(); 240 | } catch (\Throwable $e) { 241 | $logger = $this->protocol->getClient()->getLogger(); 242 | $logger->critical(sprintf( 243 | '[soluble-japha] %s (%s)', 244 | $e->getMessage(), 245 | __METHOD__ 246 | )); 247 | throw new ConnectionException($e->getMessage(), $e->getCode()); 248 | } 249 | stream_set_timeout($socket, -1); 250 | 251 | return new SocketChannelP($socket, $this->host, $this->java_recv_size, $this->java_send_size); 252 | } 253 | 254 | public function keepAlive(): void 255 | { 256 | parent::keepAlive(); 257 | } 258 | 259 | public function redirect(): void 260 | { 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # soluble-japha 2 | 3 | [![PHP Version](https://img.shields.io/badge/php-5.6+-ff69b4.svg)](https://packagist.org/packages/soluble/japha) 4 | [![PHP Version](https://img.shields.io/badge/php-7.4+-ff69b4.svg)](https://packagist.org/packages/soluble/japha) 5 | [![PHP Version](https://img.shields.io/badge/php-8.0+-ff69b4.svg)](https://packagist.org/packages/soluble/japha) 6 | [![Build Status](https://travis-ci.org/belgattitude/soluble-japha.svg?branch=master)](https://travis-ci.org/belgattitude/soluble-japha) 7 | [![codecov](https://codecov.io/gh/belgattitude/soluble-japha/branch/master/graph/badge.svg)](https://codecov.io/gh/belgattitude/soluble-japha) 8 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/belgattitude/soluble-japha/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/belgattitude/soluble-japha/?branch=master) 9 | [![Latest Stable Version](https://poser.pugx.org/soluble/japha/v/stable.svg)](https://packagist.org/packages/soluble/japha) 10 | [![Total Downloads](https://poser.pugx.org/soluble/japha/downloads.png)](https://packagist.org/packages/soluble/japha) 11 | [![License](https://poser.pugx.org/soluble/japha/license.png)](https://packagist.org/packages/soluble/japha) 12 | 13 | In short **soluble-japha** allows to write Java code in PHP and interact with the JVM ecosystem. 14 | 15 | ---- 16 | 17 | > Read the doc on [https://belgattitude.github.io/soluble-japha](https://belgattitude.github.io/soluble-japha) 18 | > website for complete information 19 | 20 | ---- 21 | 22 | As meaningless examples: 23 | 24 | ```php 25 | java('java.util.HashMap', [ 28 | 'message' => 'Hello world', 29 | 'value' => $ba->java('java.math.BigInteger', PHP_INT_MAX) 30 | ]); 31 | $hashMap->put('message', '你好,世界'); 32 | echo $hashMap->get('message'); 33 | ``` 34 | 35 | ------- 36 | 37 | ```php 38 | java('an.imaginary.JavaLibraryClass', 'param1', 'param2'); 46 | 47 | $results = $javaLib->aMethodOnJavaLibExecutedOnTheJVM( 48 | // Method parameters 49 | $hashMap->get('message'), 50 | $ba->java('java.io.BufferedReader', 51 | $ba->java('java.io.FileReader', __FILE__) 52 | ), 53 | $ba->javaClass('java.util.TimeZone')->SHORT 54 | ); 55 | 56 | foreach ($results as $key => $values) { 57 | echo "$key: " . $values[0] . PHP_EOL; 58 | } 59 | } catch (Exception\ClassNotFoundException $e) { 60 | echo $e->getMessage(); 61 | } catch (Exception\JavaException $e) { 62 | echo $e->getMessage() . ' [' . $e->getJavaClassName() . ']'; 63 | echo $e->getStackTrace(); 64 | } 65 | 66 | ``` 67 | 68 | And if you're wondering what's the `$ba` object, it's a connection 69 | to the java bridge server: 70 | 71 | ```php 72 | 'localhost:8080/MyJavaBridge/servlet.phpjavabridge' 79 | ]; 80 | 81 | try { 82 | $ba = new BridgeAdapter($options); 83 | } catch (BridgeException\ConnectionException $e) { 84 | // Server is not reachable 85 | echo $e->getMessage(); 86 | } 87 | ``` 88 | 89 | ## Use cases 90 | 91 | **Expand the PHP horizons to the Java ecosystem**, especially whenever you want 92 | to take advantage of 93 | 94 | - some compelling libraries *([Jasperreports](http://community.jaspersoft.com/project/jasperreports-library), [CoreNLP](http://stanfordnlp.github.io/CoreNLP/), [FlyingSaucer](https://github.com/flyingsaucerproject/flyingsaucer/releases), [Jsoup](https://jsoup.org/)...)* 95 | - benefit from JVM performances *([Deeplearning4J](https://deeplearning4j.org/)...)* or wrappers *([TensorFlow Java Api](https://www.tensorflow.org/api_docs/)...)*. 96 | - when a pure-PHP alternative does not exists *(Android, driver, closed api, enterprise...)* 97 | - or simply for the fun of it. 98 | 99 | > The freedom allowed by `soluble-japha` is not fit for every scenarios. 100 | > Be sure to read the [considerations](#considerations) and [performance](#performance) 101 | > sections to learn more. 102 | 103 | ## Features 104 | 105 | `soluble-japha` provides a PHP client to interact with the Java Virtual Machine. 106 | 107 | - [x] Write Java code from PHP *(in a similar way from equivalent java code)*. 108 | - [x] Keep *programmatic* code control from the PHP side *([function oriented vs REST](#considerations))*. 109 | - [x] Java execution on the JVM ensuring compatibility and efficiency *(proxied objects)*. 110 | - [x] No need to write a service layer prior to usage *(the Java object is the contract)*. 111 | - [x] Fast network based communication between runtimes, no JVM startup effort. 112 | - [x] Solid foundation to create, develop *or publish* PHP wrappers over java libs. 113 | 114 | 115 | > For user of previous versions, **soluble-japha** client replaces the original/legacy [PHPJavaBridge](http://php-java-bridge.sourceforge.net/pjb/) 116 | > `Java.inc` implementation and has been completely refactored to fit modern practices 117 | > and PHP7. 118 | > See the [differences here](./doc/notes_legacy.md) and the [legacy compatibility layer](https://github.com/belgattitude/soluble-japha-pjb62-compat) if needed. 119 | 120 | ## Requirements 121 | 122 | - Version `^3.0` requires PHP 8.0 ![PHP Version](http://img.shields.io/badge/php-8.0+-ff69b4.svg) 123 | - Version `^2.0` requires PHP 7.1 ![PHP Version](http://img.shields.io/badge/php-7.1+-ff69b4.svg) 124 | 125 | > **Important**. There's **NO API BC-BREAK** between v0.13, v1.x, v2.x and v3.x so you should be 126 | > able to upgrade safely between releases. The choice to increment version numbers to drop 127 | > support for older php versions was made to avoid any confusion with multiple php installs. 128 | 129 | 130 | If you're looking for compatibility with older PHP versions, note that: 131 | 132 | - Version `^1.0` requires PHP 5.6 ![PHP Version](http://img.shields.io/badge/php-5.6+-ff69b4.svg) and works with HHVM. 133 | - Version `^0.13` requires PHP 5.5 ![PHP Version](http://img.shields.io/badge/php-5.5+-ff69b4.svg) and works with HHVM. 134 | 135 | 136 | ## Documentation 137 | 138 | - Go to https://belgattitude.github.io/soluble-japha 139 | 140 | ## Installation 141 | 142 | Installation in your PHP project **(client)** 143 | 144 | ```console 145 | $ composer require soluble/japha 146 | ``` 147 | 148 | ## Considerations 149 | 150 | > In short, **the bridge shines** whenever you need to use directly a Java library 151 | > within a reasonable number of method calls. Otherwise implement 152 | > **REST or RPC approaches** for first-class system integrations. 153 | 154 | The soluble-japha bridge can be seen as a `function oriented` solution in 155 | comparison to `resource oriented` ones *(i.e. REST,...)*. From REST or even 156 | RPC-based solutions *(XMLRPC, JsonRPC or [gRPC](https://github.com/grpc/grpc))*, 157 | the bridge skips the need to write a service layer on 158 | the Java side and allows a more *programmatic* approach to PHP developers. 159 | 160 | Depending on usage, the benefits of freedom offered by the bridge 161 | can become a limitation in term of performance. Keep in mind that 162 | the bridge is sensitive to the number of objects and method calls 163 | (named `roundtrips`) and if few hundreds of methods calls are 164 | often insignificant (a `roundtrip` is generally less than 0.1ms) going further 165 | its target scenarios can be disappointing. In those case, 166 | traditional approaches like REST should be considered and applied instead. 167 | 168 | That said, the bridge is a good, reliable and sometimes preferable alternative 169 | over REST for scenarios where a reasonable number of methods calls is intended. 170 | 171 | Be sure to read the 172 | - [http://docs.soluble.io/soluble-japha/bridge_how_it_works/](http://docs.soluble.io/soluble-japha/bridge_how_it_works/) 173 | - [http://docs.soluble.io/soluble-japha/bridge_benchmarks/](http://docs.soluble.io/soluble-japha/bridge_benchmarks/) 174 | 175 | ## Support 176 | 177 | Please fill any issues on the [offical tracker](https://github.com/belgattitude/soluble-japha/issues). 178 | If you like to contribute, see the [contribution guidelines](https://github.com/belgattitude/soluble-japha/blob/master/CONTRIBUTING.md). 179 | All P/R are warmly welcomed. 180 | 181 | 182 | ### Credits 183 | 184 | * This code is principally developed and maintained by [Sébastien Vanvelthem](https://github.com/belgattitude). 185 | * Special thanks to [all of these awesome contributors](https://github.com/belgattitude/soluble-japha/network/members) 186 | * This project is based on the Java.inc work made by the [PHPJavaBridge developers](http://php-java-bridge.sourceforge.net/pjb/contact.php#code_contrib). 187 | 188 | ### Special mention 189 | 190 | Grateful thanks to JetBrains for granting an opensource license of PHPStorm and Idea. Really recommend !!! 191 | 192 | [![PHPStorm](./doc/images/phpstorm.svg)](https://www.jetbrains.com) 193 | 194 | ## Coding standards and interop 195 | 196 | * [PSR 4 Autoloader](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) 197 | * [PSR 3 Logger interface](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) 198 | * [PSR 2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) 199 | * [PSR 1 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) 200 | 201 | -------------------------------------------------------------------------------- /src/Soluble/Japha/Bridge/Driver/Pjb62/AbstractJava.php: -------------------------------------------------------------------------------- 1 | __factory->create($this->__java, $this->__signature); 74 | $this->__delegate = $proxy; 75 | $this->__java = $proxy->__java; 76 | $this->__signature = $proxy->__signature; 77 | } 78 | 79 | /** 80 | * @param string $type 81 | * 82 | * @return mixed 83 | */ 84 | public function __cast(string $type) 85 | { 86 | if (!isset($this->__delegate)) { 87 | $this->__createDelegate(); 88 | } 89 | 90 | return $this->__delegate->__cast($type); 91 | } 92 | 93 | /** 94 | * @return array 95 | */ 96 | public function __sleep() 97 | { 98 | if (!isset($this->__delegate)) { 99 | $this->__createDelegate(); 100 | } 101 | $this->__delegate->__sleep(); 102 | 103 | return ['__delegate']; 104 | } 105 | 106 | public function __wakeup() 107 | { 108 | if (!isset($this->__delegate)) { 109 | $this->__createDelegate(); 110 | } 111 | $this->__delegate->__wakeup(); 112 | $this->__java = $this->__delegate->get__java(); 113 | $this->__client = $this->__delegate->get__signature(); 114 | } 115 | 116 | /** 117 | * Delegate the magic method __get() to the java object 118 | * to access the Java object properties (and not the PHP 119 | * remote proxied object). 120 | * 121 | * @throws \Exception Depending on ThrowExceptionProxy 122 | * 123 | * @param string $key 124 | * 125 | * @return mixed 126 | */ 127 | public function __get(string $key) 128 | { 129 | if (!isset($this->__delegate)) { 130 | $this->__createDelegate(); 131 | } 132 | 133 | return $this->__delegate->__get($key); 134 | } 135 | 136 | /** 137 | * Delegate the magic method __set() to the java object 138 | * to access the Java object properties (and not the PHP 139 | * remote proxied object). 140 | * 141 | * @throws \Exception Depending on ThrowExceptionProxy 142 | * 143 | * @param string $key 144 | * @param mixed $val 145 | */ 146 | public function __set(string $key, $val): void 147 | { 148 | if (!isset($this->__delegate)) { 149 | $this->__createDelegate(); 150 | } 151 | $this->__delegate->__set($key, $val); 152 | } 153 | 154 | /** 155 | * Delegate the magic method __cal() to the java object 156 | * to access the Java object method (and not the PHP 157 | * remote proxied object). 158 | * 159 | * @throws \Exception Depending on ThrowExceptionProxy 160 | * 161 | * @param string $method 162 | * @param array $args 163 | * 164 | * @return mixed 165 | */ 166 | public function __call(string $method, array $args) 167 | { 168 | if (!isset($this->__delegate)) { 169 | $this->__createDelegate(); 170 | } 171 | 172 | return $this->__delegate->__call($method, $args); 173 | } 174 | 175 | /** 176 | * @param mixed|null ...$args arguments 177 | * 178 | * @return ObjectIterator 179 | */ 180 | public function getIterator(...$args) 181 | { 182 | if (!isset($this->__delegate)) { 183 | $this->__createDelegate(); 184 | } 185 | 186 | if (empty($args)) { 187 | return $this->__delegate->getIterator(); 188 | } 189 | 190 | return $this->__call('getIterator', $args); 191 | } 192 | 193 | /** 194 | * @param string|int $idx 195 | * @param mixed|null ...$args 196 | * 197 | * @return bool 198 | */ 199 | public function offsetExists($idx, ...$args): bool 200 | { 201 | if (!isset($this->__delegate)) { 202 | $this->__createDelegate(); 203 | } 204 | if (empty($args)) { 205 | return $this->__delegate->offsetExists($idx); 206 | } 207 | 208 | // In case we supplied more arguments than what ArrayAccess 209 | // suggest, let's try for a java method called offsetExists 210 | // with all the provided parameters 211 | array_unshift($args, $idx); // Will add idx at the beginning of args params 212 | 213 | return $this->__call('offsetExists', $args); 214 | } 215 | 216 | /** 217 | * @param string|int $idx 218 | * @param mixed|null ...$args additional arguments 219 | * 220 | * @return mixed 221 | */ 222 | public function offsetGet($idx, ...$args) 223 | { 224 | if (!isset($this->__delegate)) { 225 | $this->__createDelegate(); 226 | } 227 | if (empty($args)) { 228 | return $this->__delegate->offsetGet($idx); 229 | } 230 | array_unshift($args, $idx); 231 | 232 | return $this->__call('offsetGet', $args); 233 | } 234 | 235 | /** 236 | * @param string|int $idx 237 | * @param mixed $val 238 | * @param mixed|null ...$args additional arguments 239 | * 240 | * @return mixed 241 | */ 242 | public function offsetSet($idx, $val, ...$args) 243 | { 244 | if (!isset($this->__delegate)) { 245 | $this->__createDelegate(); 246 | } 247 | if (empty($args)) { 248 | return $this->__delegate->offsetSet($idx, $val); 249 | } 250 | 251 | array_unshift($args, $idx, $val); 252 | 253 | return $this->__call('offsetSet', $args); 254 | } 255 | 256 | /** 257 | * @param mixed $idx 258 | * @param mixed|null ...$args additional arguments 259 | * 260 | * @return mixed|void 261 | */ 262 | public function offsetUnset($idx, ...$args) 263 | { 264 | if (!isset($this->__delegate)) { 265 | $this->__createDelegate(); 266 | } 267 | if (empty($args)) { 268 | return $this->__delegate->offsetUnset($idx); 269 | } 270 | array_unshift($args, $idx); 271 | 272 | return $this->__call('offsetUnset', $args); 273 | } 274 | 275 | /** 276 | * @return int 277 | */ 278 | public function get__java(): int 279 | { 280 | return $this->__java; 281 | } 282 | 283 | /** 284 | * Return java object id. 285 | * 286 | * @return int 287 | */ 288 | public function __getJavaInternalObjectId(): int 289 | { 290 | return $this->__java; 291 | } 292 | 293 | /** 294 | * @return string 295 | */ 296 | public function get__signature(): ?string 297 | { 298 | return $this->__signature; 299 | } 300 | 301 | /** 302 | * The PHP magic method __toString() cannot be applied 303 | * on the PHP object but has to be delegated to the Java one. 304 | * 305 | * @return string 306 | */ 307 | public function __toString(): string 308 | { 309 | if (!isset($this->__delegate)) { 310 | $this->__createDelegate(); 311 | } 312 | 313 | return $this->__delegate->__toString(); 314 | } 315 | 316 | /** 317 | * Returns the runtime class of this Object. 318 | * The returned Class object is the object that is locked by static synchronized methods of the represented class. 319 | * 320 | * @return Interfaces\JavaObject Java('java.lang.Object') 321 | */ 322 | public function getClass(): Interfaces\JavaObject 323 | { 324 | return $this->__delegate->getClass(); 325 | } 326 | } 327 | --------------------------------------------------------------------------------