Completed
Push — master ( e73d65...d818c3 )
by J.D.
03:52
created

WordPoints_Entity_chang_trigger::created()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 10
rs 9.4285
1
<?php
2
3
/**
4
 * .
5
 *
6
 * @package wordpoints-hooks-api
7
 * @since   1.
8
 */
9
10
// this is basicaly a router.
11
class WordPoints_Entity_chang_trigger implements WordPoints_Entity_Change_ListenerI {
12
13
	public function created( WordPoints_Entity $entity ) {
14
		// can multiple entities be created at once? not using insert() but via query()
15
		// maybe? and how shoudl we handle that, all at once or one by one?
16
17
		/** @var WordPoints_Entity_Change_ListenerI[] $listeners */
18
		$listeners = wordpoints_apps()->sub_apps->get( 'entity_change_listeners' );
19
		foreach ( $listeners as $listener ) {
20
			$listener->created( $entity );
21
		}
22
	}
23
24
25
	public function updated( WordPoints_Entity $before, WordPoints_Entity $after ) {
26
27
		// what if multiple entities are updated at once? do we run each one separatately?
28
		// mayb ethat should be left up to the listenter.
29
		// and likewise wherther we handle the entity modifications attribute by attribute.
30
	}
31
32
33
	public function deleted( WordPoints_Entity $entity ) {
34
35
		// what if we need information about this entitie's relationships, etc., that
36
		// isn't included inthe entity object?
37
		// this is a problem for the hooks api, but not for the possession api.
38
		// but hwat about other potential apis?
39
		// of course, there is no guarantee that the relationships will be deleted
40
		// first, but there is no guarantee that they won't, either.
41
		// but i suppose that our excuse is that any API should be listening to
42
		// entity changes as well if it needs to know about such things.
43
		// but when something is deleted, then such apis will be triggered many times
44
		// right in a row. not ideal, but it isn't as if things are constantly being
45
		// deleted. but if we had a retroactive api, we could just temporarily
46
		// suspend running the listeners and then trigger things after all of the
47
		// queries were done. for a single entity it wouldn't be worth it, but maybe
48
		// for a bunch of entities deleted at once. but that isn't a cross-api think,
49
		// just mainly for the hooks/possession apis. though really is suppose that
50
		// it sis a part of the entity api, in terms of the queries, but we have to
51
		// have some sor of logs in order to be able to use it idempotently.
52
53
		// just running before delte wouldn't work either, because the relatinships
54
		// would have alrady been severed then if they are severed before deletion.
55
	}
56
57
}
58
59
// this is basically a reactor
60
interface WordPoints_Entity_Change_ListenerI {
61
	public function created( WordPoints_Entity $entity );
62
	public function updated( WordPoints_Entity $before, WordPoints_Entity $after );
63
	public function deleted( WordPoints_Entity $entity );
64
}
65
66
class WordPoints_Entity_Change_Listener_Hooks implements WordPoints_Entity_Change_ListenerI {
67
68
	public function created( WordPoints_Entity $entity ) {
69
70
		// maybe we would have multuiple events with requirements for a single entity?
71
		// like comment author and post commentn author targets for the same hook.
72
		// but I guess taht is just one event.
73
		// what about user register vs user create on MS?
74
		if ( $this->matches_requirements( $entity ) ) {
75
			$this->fire_event( 'add', $entity );
76
		}
77
	}
78
79
	public function updated( WordPoints_Entity $before, WordPoints_Entity $after ) {
80
81
		if ( $this->matches_requirements( $after ) ) {
82
			if ( ! $this->matches_requirements( $before ) ) {
83
				$this->fire_event( 'add', $after );
84
			}
85
		} else {
86
			if ( $this->matches_requirements( $before ) ) {
87
				$this->fire_event( 'remove', $after );
88
			}
89
		}
90
	}
91
92
	public function deleted( WordPoints_Entity $entity ) {
93
94
		if ( $this->matches_requirements( $entity ) ) {
95
			$this->fire_event( 'remove', $entity );
96
		}
97
	}
98
99
	// ideally we would actually check the conditions for each reaction here.
100
	// however, that will likely just have to be a bug/edge-case until the new
101
	// api is introduced.
102
	// ahve we ever considered what happens when an entity has been modified to not
103
	// match the conditions anymore before it is deleted? but that doesn't pose a
104
	// problem in teh hooks api usually, because before we were just reversing based
105
	// on the hook logs. so we didnt' check if the entity matched teh conditions at
106
	// all.
107
	// so the whole issue with relationships being deleted before the hooks api was
108
	// called into play when the entity itself is deleted is moot, because the
109
	// reltionships, etc., aren't even taken into account when toggle-off is called,
110
	// in the points reactor. other reators might, i guess.
111
	protected function matches_requirements( WordPoints_Entity $entity ) {
112
113
		/** @var WordPoints_Class_Registry $defaults */
114
		$defaults = wordpoints_apps()->sub_apps->get( 'entity_possession_defaults' );
115
116
		$defaults = $defaults->get( $entity->get_slug() );
117
118
		if ( ! $defaults ) {
119
			return false;
120
		}
121
122
		// use conditions here?
123
		foreach ( $defaults as $child => $value ) {
124
			if ( $entity->get_child( $child ) !== $value ) {
125
				return false;
126
			}
127
		}
128
129
		return true;
130
	}
131
132
	protected function fire_event( $type, WordPoints_Entity $entity ) {
133
134
		$args = new WordPoints_Hook_Event_Args( array() );
135
		$args->add_entity( $entity );
136
137
		wordpoints_hooks()->fire(
138
			$type . '_entity_' . $entity->get_slug(),
139
			$args,
140
			'toggle_on'
141
		);
142
	}
143
}
144
145
class WordPoints_Entity_Change_Listener_Points implements WordPoints_Entity_Change_ListenerI {
146
147
	public function created( WordPoints_Entity $entity ) {
148
149
		$this->process_entityish(
150
			$entity
151
			, $this->get_settings_for_entity( $entity )
0 ignored issues
show
Bug introduced by
The method get_settings_for_entity() does not seem to exist on object<WordPoints_Entity_Change_Listener_Points>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
152
		);
153
	}
154
155
	protected function process_entityish( WordPoints_EntityishI $entity, $settings ) {
156
157
		// possibly make this more like extension handling
158
		if ( ! $this->meets_conditions( $entity, $settings['conditions'] ) ) {
159
			return;
160
		}
161
162
		// possibly make this more like reactor handling.
163
		$this->award_points( $settings );
164
165
		// only proces the attributes taht have changed.
166
		// acutally, in this case, the entity was just created.
167
		if ( $entity instanceof WordPoints_Entity_ParentI ) {
168
169
			// this check runs on attributes only.
170
			foreach ( $settings['children'] as $child_slug => $child_settings ) {
171
				$this->process_entityish( $entity->get_child( $child_slug ), $child_settings );
172
			}
173
174
			// we also need to check for any children, like relationships, that have
175
			// the settings stored separately.
176
			// for this we need a list of relationships.
177
			// if we do two-way relationships, we may need to have infinite-loop
178
			// halding here, depending.
179
			foreach ( $this->get_related_entities( $entity ) as $child_entity ) {
180
				// If a comment was just created, for example, and we have conditions
181
				// on the post entity, that affect the comment author, this allows
182
				// us to handle those by pulling up the settings for the post entity
183
				// and looping through them too.
184
				// however, we need to limit this to only awarding the comment author
185
				// don't we? But maybe not, because what about when the post awards
186
				// are conditioned on the post's comments? but then the data would
187
				// just be on the comment but with the post author as the target.
188
				// But we still need to have some way of limiting this to only
189
				// targets relating to the comment.
190
				// I suppose taht maybe it is as simple as continuing to pass the
191
				// entity hierarchy with the comment at the top around, which when
192
				// we attempt to get the target with get_from_hierarchy() will only
193
				// return a value if the target is the comment author. That would be
194
				// even better/easier if the settings were indexed by target somehow
195
				// as we've proposed...
196
				$this->process_entityish(
197
					$child_entity
198
					, $this->get_entity_settings( $child_entity )
0 ignored issues
show
Bug introduced by
The method get_entity_settings() does not seem to exist on object<WordPoints_Entity_Change_Listener_Points>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
199
				);
200
			}
201
		}
202
	}
203
204
	// do we also listen for relationship changes, or is that a separate api?
205
	// I guess we do listen for some, but only because they happen to be defined on
206
	// entity attributes.
207
208
	// and are these only whole entities, or can they be just atts? I guess taht
209
	// doesn't really make sense.
210
	// I think relationships can be created and deleted, but not really updated.
211
	// so maybe what we need is a separate api?
212
	public function updated( WordPoints_Entity $before, WordPoints_Entity $after ) {
213
214
		$settings = $this->get_settings_for_entity( $before );
0 ignored issues
show
Bug introduced by
The method get_settings_for_entity() does not seem to exist on object<WordPoints_Entity_Change_Listener_Points>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
215
216
		/** @var WordPoints_Class_Registry_ChildrenI $children */
217
		$children = wordpoints_entities()->children;
0 ignored issues
show
Documentation introduced by
The property children does not exist on object<WordPoints_App_Registry>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
218
		foreach ( $children->get_children_slugs( $before->get_slug() ) as $child_slug ) {
219
			
220
			if ( ! isset( $settings['children'][ $child_slug ] ) ) {
221
				continue;
222
			}
223
			
224
			// which ones are attributes and which are not?
225
			// I guess we just check the atts.
226
			// but it isn't as simple as that, because the atts don't necessarilly
227
			// match the names of the children.
228
			// so we could just pull up the child and check if it is an instanceof
229
			// the correct class. that seems expensive, but we need them anyway (see
230
			// below), and maybe if we have a list of relatinoships we could check
231
			// that too.
232
			// but then we'll not be checking even the relationships taht are defined
233
			// on the atts. so we have to decide whether those should be handled by
234
			// a separate api or not.
235
			if ( $before->get_the_attr_value( $child_slug ) === $after->get_the_attr_value( $child_slug ) ) {
236
				continue;
237
			}
238
239
			$this->process_modified_entityish(
240
				$before->get_child( $child_slug )
0 ignored issues
show
Security Bug introduced by
It seems like $before->get_child($child_slug) targeting WordPoints_Entity::get_child() can also be of type false; however, WordPoints_Entity_Change...ss_modified_entityish() does only seem to accept object<WordPoints_EntityishI>, did you maybe forget to handle an error condition?
Loading history...
241
				, $after->get_child( $child_slug )
0 ignored issues
show
Security Bug introduced by
It seems like $after->get_child($child_slug) targeting WordPoints_Entity::get_child() can also be of type false; however, WordPoints_Entity_Change...ss_modified_entityish() does only seem to accept object<WordPoints_EntityishI>, did you maybe forget to handle an error condition?
Loading history...
242
				, $settings['children'][ $child_slug ]
243
			);
244
		}
245
	}
246
247
	protected function process_modified_entityish( WordPoints_EntityishI $before, WordPoints_EntityishI $after, $settings ) {
248
249
		if ( ! $this->meets_conditions( $before, $settings['conditions'] ) ) {
250
			if ( $this->meets_conditions( $after, $settings['conditions'] ) ) {
251
				$this->award_points( $settings );
252
253
				// also need to process other eneities that are affected by this
254
				// change, which may have conditions on this, i.e., that could be
255
				// parents of this enity.
256
				// that's actually only true for relationship/cascading handling.
257
				// otherwise we are actually fine here, since parent/child entities
258
				// will have points awarded by the targets.
259
				// but say taht this is a post, and on the user entity there is a
260
				// condition that the comment authors are to be awarded based on a
261
				// certain attribute of the post author. if the post author has
262
				// changed, that needs to be processed. however, that is a
263
				// relationship change, so maybe it will come thorugh a different
264
				// api. if not, we need to check if this is a relationship here and
265
				// then we do indeed need to run through the children/parents.
266
			}
267
		} else {
268
			if ( ! $this->meets_conditions( $after, $settings['conditions'] ) ) {
269
				$this->remove_points( $settings );
0 ignored issues
show
Bug introduced by
The method remove_points() does not seem to exist on object<WordPoints_Entity_Change_Listener_Points>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
270
			}
271
		}
272
	}
273
274
	public function deleted( WordPoints_Entity $entity ) {
275
		// basically the opposite of created().
276
		$this->process_entityish_reverse( $entity );
0 ignored issues
show
Bug introduced by
The method process_entityish_reverse() does not exist on WordPoints_Entity_Change_Listener_Points. Did you maybe mean process_entityish()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
277
	}
278
279
	protected function meets_conditions( WordPoints_EntityishI $entityish, $conditions ) {
0 ignored issues
show
Unused Code introduced by
The parameter $entityish is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $conditions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
280
		// use conditions api
281
282
		return false;
283
	}
284
285
	private function award_points( $settings ) {
286
287
		$hierarchy = new WordPoints_Entity_Hierarchy( $this->eitnty );
0 ignored issues
show
Bug introduced by
The property eitnty does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
288
		// todo we'll need to introudce entity array targets, possibly.
289
		// how will we know how to reverse the target reltinoship chains in the UI
290
		// before saving? i guess we'll need to either have a dedicated index fo taht
291
		// or else juust look it up by looping through the relationships (though we'd
292
		// have to remove the entity array {} part from any one-to-many relationships
293
		// and maybe we'd have to add it to others?)
294
		$targets = $hierarchy->get_from_hierarchy( $settings['target'] );
0 ignored issues
show
Unused Code introduced by
$targets is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
295
296
		// maybe just one target, maybe several, depending.
297
	}
298
299
	private function get_related_entities( WordPoints_Entity $entity ) {
0 ignored issues
show
Unused Code introduced by
The parameter $entity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
300
301
		// what we need is parental conditions. Conditions that go up the chain,
302
		// and look back at parent entities. This way we can handle the info about
303
		// comments on a particuar post when the post entity is modified, since the
304
		// conditions can be on the post
305
		// we'd need inverse relationships, get all comements for the post, and then
306
		// run the "paretn" conditions on each of them.
307
		// or just not allow conditions/reactions on relationships
308
		// but just registering relationships for both entities is not enough,
309
		// because we can't tell the slug of the entity form the slug of the entity
310
		// child, unless we just loop thorugh them and attempt to get that entity
311
		// from each relationship.
312
313
		return array();
314
	}
315
}
316
317
// EOF
318