PageRenderTime 20ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/applications/feed/publisher/PhabricatorFeedStoryPublisher.php

http://github.com/facebook/phabricator
PHP | 133 lines | 78 code | 19 blank | 36 comment | 6 complexity | c5d6cee8f981a59971e2af28dd6f5e4e MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /*
  3. * Copyright 2011 Facebook, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. final class PhabricatorFeedStoryPublisher {
  18. private $relatedPHIDs;
  19. private $storyType;
  20. private $storyData;
  21. private $storyTime;
  22. private $storyAuthorPHID;
  23. public function setRelatedPHIDs(array $phids) {
  24. $this->relatedPHIDs = $phids;
  25. return $this;
  26. }
  27. public function setStoryType($story_type) {
  28. $this->storyType = $story_type;
  29. return $this;
  30. }
  31. public function setStoryData(array $data) {
  32. $this->storyData = $data;
  33. return $this;
  34. }
  35. public function setStoryTime($time) {
  36. $this->storyTime = $time;
  37. return $this;
  38. }
  39. public function setStoryAuthorPHID($phid) {
  40. $this->storyAuthorPHID = $phid;
  41. return $this;
  42. }
  43. public function publish() {
  44. if (!$this->relatedPHIDs) {
  45. throw new Exception("There are no PHIDs related to this story!");
  46. }
  47. if (!$this->storyType) {
  48. throw new Exception("Call setStoryType() before publishing!");
  49. }
  50. $chrono_key = $this->generateChronologicalKey();
  51. $story = new PhabricatorFeedStoryData();
  52. $story->setStoryType($this->storyType);
  53. $story->setStoryData($this->storyData);
  54. $story->setAuthorPHID($this->storyAuthorPHID);
  55. $story->setChronologicalKey($chrono_key);
  56. $story->save();
  57. $ref = new PhabricatorFeedStoryReference();
  58. $sql = array();
  59. $conn = $ref->establishConnection('w');
  60. foreach (array_unique($this->relatedPHIDs) as $phid) {
  61. $sql[] = qsprintf(
  62. $conn,
  63. '(%s, %s)',
  64. $phid,
  65. $chrono_key);
  66. }
  67. queryfx(
  68. $conn,
  69. 'INSERT INTO %T (objectPHID, chronologicalKey) VALUES %Q',
  70. $ref->getTableName(),
  71. implode(', ', $sql));
  72. return $story;
  73. }
  74. /**
  75. * We generate a unique chronological key for each story type because we want
  76. * to be able to page through the stream with a cursor (i.e., select stories
  77. * after ID = X) so we can efficiently perform filtering after selecting data,
  78. * and multiple stories with the same ID make this cumbersome without putting
  79. * a bunch of logic in the client. We could use the primary key, but that
  80. * would prevent publishing stories which happened in the past. Since it's
  81. * potentially useful to do that (e.g., if you're importing another data
  82. * source) build a unique key for each story which has chronological ordering.
  83. *
  84. * @return string A unique, time-ordered key which identifies the story.
  85. */
  86. private function generateChronologicalKey() {
  87. // Use the epoch timestamp for the upper 32 bits of the key. Default to
  88. // the current time if the story doesn't have an explicit timestamp.
  89. $time = nonempty($this->storyTime, time());
  90. // Generate a random number for the lower 32 bits of the key.
  91. $rand = head(unpack('L', Filesystem::readRandomBytes(4)));
  92. // On 32-bit machines, we have to get creative.
  93. if (PHP_INT_SIZE < 8) {
  94. // We're on a 32-bit machine.
  95. if (function_exists('bcadd')) {
  96. // Try to use the 'bc' extension.
  97. return bcadd(bcmul($time, bcpow(2, 32)), $rand);
  98. } else {
  99. // Do the math in MySQL. TODO: If we formalize a bc dependency, get
  100. // rid of this.
  101. $conn_r = id(new PhabricatorFeedStoryData())->establishConnection('r');
  102. $result = queryfx_one(
  103. $conn_r,
  104. 'SELECT (%d << 32) + %d as N',
  105. $time,
  106. $rand);
  107. return $result['N'];
  108. }
  109. } else {
  110. // This is a 64 bit machine, so we can just do the math.
  111. return ($time << 32) + $rand;
  112. }
  113. }
  114. }