{"action":"created","comment":{"url":"https://api.github.com/repos/translate/translate/pulls/comments/172050567","pull_request_review_id":101007460,"id":172050567,"diff_hunk":"@@ -216,171 +339,88 @@ def serialize(self, out):\n \n     def parse(self, phpsrc):\n         \"\"\"Read the source of a PHP file in and include them as units.\"\"\"\n-        newunit = phpunit()\n-        lastvalue = \"\"\n-        value = \"\"\n-        invalue = False\n-        incomment = False\n-        inarray = False\n-        valuequote = \"\"  # Either ' or \".\n-        equaldel = \"=\"\n-        enddel = \";\"\n-        prename = \"\"\n-        keys_dict = {}\n-        line_number = 0\n-\n-        # For each line in the PHP translation file.\n-        for line in phpsrc.decode(self.encoding).split(\"\\n\"):\n-            line_number += 1\n-            commentstartpos = line.find(\"/*\")\n-            commentendpos = line.rfind(\"*/\")\n-\n-            # If a multiline comment starts in the current line.\n-            if commentstartpos != -1:\n-                incomment = True\n-\n-                # If a comment ends in the current line.\n-                if commentendpos != -1:\n-                    newunit.addnote(line[commentstartpos:commentendpos+2],\n-                                    \"developer\")\n-                    incomment = False\n-                else:\n-                    newunit.addnote(line[commentstartpos:], \"developer\")\n-\n-            # If this a multiline comment that ends in the current line.\n-            if commentendpos != -1 and incomment:\n-                newunit.addnote(line[:commentendpos+2], \"developer\")\n-                incomment = False\n-\n-            # If this is a multiline comment which started in a previous line.\n-            if incomment and commentstartpos == -1:\n-                newunit.addnote(line, \"developer\")\n-                continue\n-\n-            # If an array starts in the current line and is using array syntax\n-            if (line.lower().replace(\" \", \"\").find('array(') != -1 and\n-                line.lower().replace(\" \", \"\").find('array()') == -1):\n-                eqpos = line.find('=')\n-                # If this is a nested array.\n-                if inarray:\n-                    prename = prename + line[:eqpos].strip() + \"->\"\n-                else:\n-                    equaldel = \"=>\"\n-                    enddel = \",\"\n-                    inarray = True\n-                    if eqpos != -1:\n-                        prename = prename + line[:eqpos].strip() + \"->\"\n-                continue\n-\n-            # If an array ends in the current line, reset variables to default\n-            # values.\n-            if inarray and line.find(');') != -1:\n-                equaldel = \"=\"\n-                enddel = \";\"\n-                inarray = False\n-                prename = \"\"\n-                continue\n-\n-            # If a nested array ends in the current line, reset prename to its\n-            # parent array default value by stripping out the last part.\n-            if inarray and line.find('),') != -1:\n-                prename = re.sub(r'[^>]+->$', '', prename)\n-                continue\n-\n-            # If the current line hosts a define syntax translation.\n-            if line.lstrip().startswith(\"define(\"):\n-                equaldel = \",\"\n-                enddel = \");\"\n-\n-            equalpos = line.find(equaldel)\n-            hashpos = line.find(\"#\")\n-            doubleslashpos = line.lstrip().find(\"//\")\n-\n-            # If this is a '#' comment line or a '//' comment that starts at\n-            # the line begining.\n-            if 0 <= hashpos < equalpos or doubleslashpos == 0:\n-                # Assume that this is a '#' comment line\n-                newunit.addnote(line.strip(), \"developer\")\n-                continue\n-\n-            # If equalpos is present in the current line and this line is not\n-            # part of a multiline translation.\n-            if equalpos != -1 and not invalue:\n-                # Get the quoting character which encloses the translation\n-                # (either ' or \").\n-                valuequote = line[equalpos+len(equaldel):].lstrip()[0]\n-\n-                if valuequote in ['\"', \"'\"]:\n-                    # Get the location for the translation unit. prename is the\n-                    # array name, or blank if no array is present. The line\n-                    # (until the equal delimiter) is appended to the location.\n-                    location = prename + line[:equalpos].strip()\n-\n-                    # Check for duplicate entries.\n-                    if location in keys_dict.keys():\n-                        # TODO Get the logger from the code that is calling\n-                        # this class.\n-                        logging.error(\"Duplicate key %s in %s:%d, first \"\n-                                      \"occurrence in line %d\", location,\n-                                      self.filename, line_number,\n-                                      keys_dict[location])\n-                    else:\n-                        keys_dict[location] = line_number\n-\n-                    # Add the location to the translation unit.\n-                    newunit.addlocation(location)\n-\n-                    # Save the translation in the value variable.\n-                    value = line[equalpos+len(equaldel):].lstrip()[1:]\n-                    lastvalue = \"\"\n-                    invalue = True\n-            else:\n-                # If no equalpos is present in the current line, but this is a\n-                # multiline translation.\n-                if invalue:\n-                    value = line\n-\n-            # Get the end delimiter position.\n-            enddelpos = value.rfind(enddel)\n-\n-            # Process the current line until all entries on it are parsed.\n-            while enddelpos != -1:\n-                # Check if the latest non-whitespace character before the end\n-                # delimiter is the valuequote.\n-                if value[:enddelpos].rstrip()[-1] == valuequote:\n-                    # Save the value string without trailing whitespaces and\n-                    # without the ending quotes.\n-                    newunit.value = lastvalue + value[:enddelpos].rstrip()[:-1]\n-                    newunit.escape_type = valuequote\n-                    lastvalue = \"\"\n-                    invalue = False\n-\n-                # If there is more text (a comment) after the translation.\n-                if not invalue and enddelpos != (len(value) - 1):\n-                    commentinlinepos = value.find(\"//\", enddelpos)\n-                    if commentinlinepos != -1:\n-                        newunit.addnote(value[commentinlinepos+2:].strip(),\n-                                        \"developer\")\n-\n-                # If the translation is already parsed, save it and initialize\n-                # a new translation unit.\n-                if not invalue:\n-                    self.addunit(newunit)\n-                    value = \"\"\n-                    newunit = phpunit()\n-\n-                # Update end delimiter position to the previous last appearance\n-                # of the end delimiter, because it might be several entries in\n-                # the same line.\n-                enddelpos = value.rfind(enddel, 0, enddelpos)\n-            else:\n-                # After processing current line, if we are not in an array,\n-                # fall back to default dialect (PHP simple variable syntax).\n-                if not inarray:\n-                    equaldel = \"=\"\n-                    enddel = \";\"\n-\n-            # If this is part of a multiline translation, just append it to the\n-            # previous translation lines.\n-            if invalue:\n-                lastvalue = lastvalue + value + \"\\n\"\n+        def newunit(name, value, escape_type, comments):\n+            newunit = phpunit()","path":"translate/storage/php.py","position":null,"original_position":318,"commit_id":"fbb740ed61777d2652954e9310a4cb48eccebc8f","original_commit_id":"88dd3492fac453dfbcaaad7b6ca932d98254854d","user":{"login":"unho","id":939880,"avatar_url":"https://avatars1.githubusercontent.com/u/939880?v=4","gravatar_id":"","url":"https://api.github.com/users/unho","html_url":"https://github.com/unho","followers_url":"https://api.github.com/users/unho/followers","following_url":"https://api.github.com/users/unho/following{/other_user}","gists_url":"https://api.github.com/users/unho/gists{/gist_id}","starred_url":"https://api.github.com/users/unho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unho/subscriptions","organizations_url":"https://api.github.com/users/unho/orgs","repos_url":"https://api.github.com/users/unho/repos","events_url":"https://api.github.com/users/unho/events{/privacy}","received_events_url":"https://api.github.com/users/unho/received_events","type":"User","site_admin":false},"body":"Nit: Use `self.UnitClass()` instead of `phpunit()`.","created_at":"2018-03-04T14:29:35Z","updated_at":"2018-03-04T14:29:35Z","html_url":"https://github.com/translate/translate/pull/3697#discussion_r172050567","pull_request_url":"https://api.github.com/repos/translate/translate/pulls/3697","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/translate/translate/pulls/comments/172050567"},"html":{"href":"https://github.com/translate/translate/pull/3697#discussion_r172050567"},"pull_request":{"href":"https://api.github.com/repos/translate/translate/pulls/3697"}}},"pull_request":{"url":"https://api.github.com/repos/translate/translate/pulls/3697","id":143432907,"html_url":"https://github.com/translate/translate/pull/3697","diff_url":"https://github.com/translate/translate/pull/3697.diff","patch_url":"https://github.com/translate/translate/pull/3697.patch","issue_url":"https://api.github.com/repos/translate/translate/issues/3697","number":3697,"state":"open","locked":false,"title":"Use phply for parsing PHP files","user":{"login":"nijel","id":212189,"avatar_url":"https://avatars0.githubusercontent.com/u/212189?v=4","gravatar_id":"","url":"https://api.github.com/users/nijel","html_url":"https://github.com/nijel","followers_url":"https://api.github.com/users/nijel/followers","following_url":"https://api.github.com/users/nijel/following{/other_user}","gists_url":"https://api.github.com/users/nijel/gists{/gist_id}","starred_url":"https://api.github.com/users/nijel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nijel/subscriptions","organizations_url":"https://api.github.com/users/nijel/orgs","repos_url":"https://api.github.com/users/nijel/repos","events_url":"https://api.github.com/users/nijel/events{/privacy}","received_events_url":"https://api.github.com/users/nijel/received_events","type":"User","site_admin":false},"body":"Current PHP parser is really problematic, so how about using some real parser for PHP?\r\n\r\nI've tried to base on https://github.com/viraptor/phply. ~~There are some tricky parts to solve (naming of units is slightly different and comments extraction does not work), but I'd like to get some feedback before spending more time on this.~~ It tries to be as much compatible as possible, but I've decided to change comments handling, see https://github.com/translate/translate/pull/3697#issuecomment-332743699 for more details.\r\n\r\nThis bases on https://github.com/translate/translate/pull/3696 to get fixed some errors in tests.\r\n\r\nFixes #1685\r\nFixes #1898\r\nFixes #1929\r\nFixes #2611\r\nFixes #2612\r\nFixes #2647\r\nFixes #2648\r\nFixes #3608\r\nFixes #3625\r\nFixes #3626\r\nFixes #3627\r\nFixes #3628\r\nFixes #3629","created_at":"2017-09-27T15:56:16Z","updated_at":"2018-03-04T14:29:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"57c634c85fcb608c5ccc8fd94d4f082245e41435","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":117348395,"url":"https://api.github.com/repos/translate/translate/labels/formats","name":"formats","color":"009800","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/translate/translate/pulls/3697/commits","review_comments_url":"https://api.github.com/repos/translate/translate/pulls/3697/comments","review_comment_url":"https://api.github.com/repos/translate/translate/pulls/comments{/number}","comments_url":"https://api.github.com/repos/translate/translate/issues/3697/comments","statuses_url":"https://api.github.com/repos/translate/translate/statuses/fbb740ed61777d2652954e9310a4cb48eccebc8f","head":{"label":"nijel:php","ref":"php","sha":"fbb740ed61777d2652954e9310a4cb48eccebc8f","user":{"login":"nijel","id":212189,"avatar_url":"https://avatars0.githubusercontent.com/u/212189?v=4","gravatar_id":"","url":"https://api.github.com/users/nijel","html_url":"https://github.com/nijel","followers_url":"https://api.github.com/users/nijel/followers","following_url":"https://api.github.com/users/nijel/following{/other_user}","gists_url":"https://api.github.com/users/nijel/gists{/gist_id}","starred_url":"https://api.github.com/users/nijel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nijel/subscriptions","organizations_url":"https://api.github.com/users/nijel/orgs","repos_url":"https://api.github.com/users/nijel/repos","events_url":"https://api.github.com/users/nijel/events{/privacy}","received_events_url":"https://api.github.com/users/nijel/received_events","type":"User","site_admin":false},"repo":{"id":4075469,"name":"translate","full_name":"nijel/translate","owner":{"login":"nijel","id":212189,"avatar_url":"https://avatars0.githubusercontent.com/u/212189?v=4","gravatar_id":"","url":"https://api.github.com/users/nijel","html_url":"https://github.com/nijel","followers_url":"https://api.github.com/users/nijel/followers","following_url":"https://api.github.com/users/nijel/following{/other_user}","gists_url":"https://api.github.com/users/nijel/gists{/gist_id}","starred_url":"https://api.github.com/users/nijel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nijel/subscriptions","organizations_url":"https://api.github.com/users/nijel/orgs","repos_url":"https://api.github.com/users/nijel/repos","events_url":"https://api.github.com/users/nijel/events{/privacy}","received_events_url":"https://api.github.com/users/nijel/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nijel/translate","description":"A collection of useful tools for localization, and a powerful API for programmers of localization tools","fork":true,"url":"https://api.github.com/repos/nijel/translate","forks_url":"https://api.github.com/repos/nijel/translate/forks","keys_url":"https://api.github.com/repos/nijel/translate/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nijel/translate/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nijel/translate/teams","hooks_url":"https://api.github.com/repos/nijel/translate/hooks","issue_events_url":"https://api.github.com/repos/nijel/translate/issues/events{/number}","events_url":"https://api.github.com/repos/nijel/translate/events","assignees_url":"https://api.github.com/repos/nijel/translate/assignees{/user}","branches_url":"https://api.github.com/repos/nijel/translate/branches{/branch}","tags_url":"https://api.github.com/repos/nijel/translate/tags","blobs_url":"https://api.github.com/repos/nijel/translate/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nijel/translate/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nijel/translate/git/refs{/sha}","trees_url":"https://api.github.com/repos/nijel/translate/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nijel/translate/statuses/{sha}","languages_url":"https://api.github.com/repos/nijel/translate/languages","stargazers_url":"https://api.github.com/repos/nijel/translate/stargazers","contributors_url":"https://api.github.com/repos/nijel/translate/contributors","subscribers_url":"https://api.github.com/repos/nijel/translate/subscribers","subscription_url":"https://api.github.com/repos/nijel/translate/subscription","commits_url":"https://api.github.com/repos/nijel/translate/commits{/sha}","git_commits_url":"https://api.github.com/repos/nijel/translate/git/commits{/sha}","comments_url":"https://api.github.com/repos/nijel/translate/comments{/number}","issue_comment_url":"https://api.github.com/repos/nijel/translate/issues/comments{/number}","contents_url":"https://api.github.com/repos/nijel/translate/contents/{+path}","compare_url":"https://api.github.com/repos/nijel/translate/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nijel/translate/merges","archive_url":"https://api.github.com/repos/nijel/translate/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nijel/translate/downloads","issues_url":"https://api.github.com/repos/nijel/translate/issues{/number}","pulls_url":"https://api.github.com/repos/nijel/translate/pulls{/number}","milestones_url":"https://api.github.com/repos/nijel/translate/milestones{/number}","notifications_url":"https://api.github.com/repos/nijel/translate/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nijel/translate/labels{/name}","releases_url":"https://api.github.com/repos/nijel/translate/releases{/id}","deployments_url":"https://api.github.com/repos/nijel/translate/deployments","created_at":"2012-04-19T13:32:40Z","updated_at":"2016-11-16T08:28:09Z","pushed_at":"2018-02-12T12:01:36Z","git_url":"git://github.com/nijel/translate.git","ssh_url":"git@github.com:nijel/translate.git","clone_url":"https://github.com/nijel/translate.git","svn_url":"https://github.com/nijel/translate","homepage":"http://translate.sourceforge.net/wiki/toolkit/index","size":29861,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"gpl-2.0","name":"GNU General Public License v2.0","spdx_id":"GPL-2.0","url":"https://api.github.com/licenses/gpl-2.0"},"forks":1,"open_issues":0,"watchers":4,"default_branch":"master"}},"base":{"label":"translate:master","ref":"master","sha":"e96c3629edbe0d59bcf80348ec5d6646c41944d3","user":{"login":"translate","id":1538178,"avatar_url":"https://avatars2.githubusercontent.com/u/1538178?v=4","gravatar_id":"","url":"https://api.github.com/users/translate","html_url":"https://github.com/translate","followers_url":"https://api.github.com/users/translate/followers","following_url":"https://api.github.com/users/translate/following{/other_user}","gists_url":"https://api.github.com/users/translate/gists{/gist_id}","starred_url":"https://api.github.com/users/translate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/translate/subscriptions","organizations_url":"https://api.github.com/users/translate/orgs","repos_url":"https://api.github.com/users/translate/repos","events_url":"https://api.github.com/users/translate/events{/privacy}","received_events_url":"https://api.github.com/users/translate/received_events","type":"Organization","site_admin":false},"repo":{"id":3769696,"name":"translate","full_name":"translate/translate","owner":{"login":"translate","id":1538178,"avatar_url":"https://avatars2.githubusercontent.com/u/1538178?v=4","gravatar_id":"","url":"https://api.github.com/users/translate","html_url":"https://github.com/translate","followers_url":"https://api.github.com/users/translate/followers","following_url":"https://api.github.com/users/translate/following{/other_user}","gists_url":"https://api.github.com/users/translate/gists{/gist_id}","starred_url":"https://api.github.com/users/translate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/translate/subscriptions","organizations_url":"https://api.github.com/users/translate/orgs","repos_url":"https://api.github.com/users/translate/repos","events_url":"https://api.github.com/users/translate/events{/privacy}","received_events_url":"https://api.github.com/users/translate/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/translate/translate","description":"Useful localization tools with Python API for building localization & translation systems","fork":false,"url":"https://api.github.com/repos/translate/translate","forks_url":"https://api.github.com/repos/translate/translate/forks","keys_url":"https://api.github.com/repos/translate/translate/keys{/key_id}","collaborators_url":"https://api.github.com/repos/translate/translate/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/translate/translate/teams","hooks_url":"https://api.github.com/repos/translate/translate/hooks","issue_events_url":"https://api.github.com/repos/translate/translate/issues/events{/number}","events_url":"https://api.github.com/repos/translate/translate/events","assignees_url":"https://api.github.com/repos/translate/translate/assignees{/user}","branches_url":"https://api.github.com/repos/translate/translate/branches{/branch}","tags_url":"https://api.github.com/repos/translate/translate/tags","blobs_url":"https://api.github.com/repos/translate/translate/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/translate/translate/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/translate/translate/git/refs{/sha}","trees_url":"https://api.github.com/repos/translate/translate/git/trees{/sha}","statuses_url":"https://api.github.com/repos/translate/translate/statuses/{sha}","languages_url":"https://api.github.com/repos/translate/translate/languages","stargazers_url":"https://api.github.com/repos/translate/translate/stargazers","contributors_url":"https://api.github.com/repos/translate/translate/contributors","subscribers_url":"https://api.github.com/repos/translate/translate/subscribers","subscription_url":"https://api.github.com/repos/translate/translate/subscription","commits_url":"https://api.github.com/repos/translate/translate/commits{/sha}","git_commits_url":"https://api.github.com/repos/translate/translate/git/commits{/sha}","comments_url":"https://api.github.com/repos/translate/translate/comments{/number}","issue_comment_url":"https://api.github.com/repos/translate/translate/issues/comments{/number}","contents_url":"https://api.github.com/repos/translate/translate/contents/{+path}","compare_url":"https://api.github.com/repos/translate/translate/compare/{base}...{head}","merges_url":"https://api.github.com/repos/translate/translate/merges","archive_url":"https://api.github.com/repos/translate/translate/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/translate/translate/downloads","issues_url":"https://api.github.com/repos/translate/translate/issues{/number}","pulls_url":"https://api.github.com/repos/translate/translate/pulls{/number}","milestones_url":"https://api.github.com/repos/translate/translate/milestones{/number}","notifications_url":"https://api.github.com/repos/translate/translate/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/translate/translate/labels{/name}","releases_url":"https://api.github.com/repos/translate/translate/releases{/id}","deployments_url":"https://api.github.com/repos/translate/translate/deployments","created_at":"2012-03-19T23:05:29Z","updated_at":"2018-03-01T14:02:27Z","pushed_at":"2018-03-04T00:35:30Z","git_url":"git://github.com/translate/translate.git","ssh_url":"git@github.com:translate/translate.git","clone_url":"https://github.com/translate/translate.git","svn_url":"https://github.com/translate/translate","homepage":"http://toolkit.translatehouse.org","size":30251,"stargazers_count":361,"watchers_count":361,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":177,"mirror_url":null,"archived":false,"open_issues_count":375,"license":{"key":"gpl-2.0","name":"GNU General Public License v2.0","spdx_id":"GPL-2.0","url":"https://api.github.com/licenses/gpl-2.0"},"forks":177,"open_issues":375,"watchers":361,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/translate/translate/pulls/3697"},"html":{"href":"https://github.com/translate/translate/pull/3697"},"issue":{"href":"https://api.github.com/repos/translate/translate/issues/3697"},"comments":{"href":"https://api.github.com/repos/translate/translate/issues/3697/comments"},"review_comments":{"href":"https://api.github.com/repos/translate/translate/pulls/3697/comments"},"review_comment":{"href":"https://api.github.com/repos/translate/translate/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/translate/translate/pulls/3697/commits"},"statuses":{"href":"https://api.github.com/repos/translate/translate/statuses/fbb740ed61777d2652954e9310a4cb48eccebc8f"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"unho"}}